[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

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



commit ff5810aea91fed15a6401f0f825bb9fbe16ca200
Merge: c2c1f5b 98cef0a
Author: Nick Mathewson <nickm@xxxxxxxxxxxxxx>
Date:   Mon Feb 7 12:47:04 2011 -0500

    Merge remote branch 'origin/maint-0.2.2'

 changes/bug2279           |   15 +++++++++++++++
 doc/spec/control-spec.txt |    6 +++++-
 doc/tor.1.txt             |   12 ++++++++++--
 src/or/config.c           |    2 ++
 src/or/connection.c       |    5 ++++-
 src/or/connection_edge.c  |   22 ++++++++++++++++++++++
 src/or/or.h               |   11 +++++++++++
 src/or/reasons.c          |    5 +++++
 8 files changed, 74 insertions(+), 4 deletions(-)

diff --combined doc/spec/control-spec.txt
index bd327db,1096245..f86f94b
--- a/doc/spec/control-spec.txt
+++ b/doc/spec/control-spec.txt
@@@ -231,7 -231,7 +231,7 @@@
           "INFO" / "NOTICE" / "WARN" / "ERR" / "NEWDESC" / "ADDRMAP" /
           "AUTHDIR_NEWDESCS" / "DESCCHANGED" / "STATUS_GENERAL" /
           "STATUS_CLIENT" / "STATUS_SERVER" / "GUARD" / "NS" / "STREAM_BW" /
 -         "CLIENTS_SEEN" / "NEWCONSENSUS" / "BUILDTIMEOUT_SET"
 +         "CLIENTS_SEEN" / "NEWCONSENSUS" / "BUILDTIMEOUT_SET" / "SIGNAL"
  
    Any events *not* listed in the SETEVENTS line are turned off; thus, sending
    SETEVENTS with an empty body turns off all event reporting.
@@@ -517,10 -517,6 +517,10 @@@
         with a $.  This is an implementation error.  It would be nice to add
         the $ back in if we can do so without breaking compatibility.]
  
 +    "traffic/read" -- Total bytes read (downloaded).
 +
 +    "traffic/written" -- Total bytes written (uploaded).
 +
      "accounting/enabled"
      "accounting/hibernating"
      "accounting/bytes"
@@@ -543,9 -539,9 +543,9 @@@
        of the form:
           OptionName SP OptionType [ SP Documentation ] CRLF
           OptionName = Keyword
 -         OptionType = "Integer" / "TimeInterval" / "DataSize" / "Float" /
 -           "Boolean" / "Time" / "CommaList" / "Dependant" / "Virtual" /
 -           "String" / "LineList"
 +         OptionType = "Integer" / "TimeInterval" / "TimeMsecInterval" /
 +           "DataSize" / "Float" / "Boolean" / "Time" / "CommaList" /
 +           "Dependant" / "Virtual" / "String" / "LineList"
           Documentation = Text
  
      "info/names"
@@@ -570,15 -566,6 +570,15 @@@
      "next-circuit/IP:port"
        XXX todo.
  
 +    "process/pid" -- Process id belonging to the main tor process.
 +    "process/uid" -- User id running the tor process, -1 if unknown (this is
 +     unimplemented on Windows, returning -1).
 +    "process/user" -- Username under which the tor process is running,
 +     providing an empty string if none exists (this is unimplemented on
 +     Windows, returning an empty string).
 +    "process/descriptor-limit" -- Upper bound on the file descriptor limit, -1
 +     if unknown.
 +
      "dir/status-vote/current/consensus" [added in Tor 0.2.1.6-alpha]
      "dir/status/authority"
      "dir/status/fp/<F>"
@@@ -1083,7 -1070,8 +1083,8 @@@
        Reason = "MISC" / "RESOLVEFAILED" / "CONNECTREFUSED" /
                 "EXITPOLICY" / "DESTROY" / "DONE" / "TIMEOUT" /
                 "NOROUTE" / "HIBERNATING" / "INTERNAL"/ "RESOURCELIMIT" /
-                "CONNRESET" / "TORPROTOCOL" / "NOTDIRECTORY" / "END"
+                "CONNRESET" / "TORPROTOCOL" / "NOTDIRECTORY" / "END" /
+                "PRIVATE_ADDR"
  
     The "REASON" field is provided only for FAILED, CLOSED, and DETACHED
     events, and only if extended events are enabled (see 3.19).  Clients MUST
@@@ -1092,7 -1080,10 +1093,10 @@@
  
        END          (We received a RELAY_END cell from the other side of this
                      stream.)
+       PRIVATE_ADDR (The client tried to connect to a private address like
+                     127.0.0.1 or 10.0.0.1 over Tor.)
        [XXXX document more. -NM]
+       
  
     The "REMOTE_REASON" field is provided only when we receive a RELAY_END
     cell, and only if extended events are enabled.  It contains the actual
@@@ -1751,27 -1742,6 +1755,27 @@@
  
    [First added in 0.2.2.7-alpha]
  
 +4.1.17. Signal received
 +
 +  The syntax is:
 +     "650" SP "SIGNAL" SP Signal CRLF
 +
 +     Signal = "RELOAD" / "DUMP" / "DEBUG" / "NEWNYM" / "CLEARDNSCACHE"
 +
 +  A signal has been received and actions taken by Tor. The meaning of each
 +  signal, and the mapping to Unix signals, is as defined in section 3.7.
 +  Future versions of Tor MAY generate signals other than those listed here;
 +  controllers MUST be able to accept them.
 +
 +  If Tor chose to ignore a signal (such as NEWNYM), this event will not be
 +  sent.  Note that some options (like ReloadTorrcOnSIGHUP) may affect the
 +  semantics of the signals here.
 +
 +  Note that the HALT (SIGTERM) and SHUTDOWN (SIGINT) signals do not currently
 +  generate any event.
 +
 +  [First added in 0.2.3.1-alpha]
 +
  5. Implementation notes
  
  5.1. Authentication
diff --combined doc/tor.1.txt
index 65dc769,3e92ef6..98415e8
--- a/doc/tor.1.txt
+++ b/doc/tor.1.txt
@@@ -343,13 -343,6 +343,13 @@@ Other options can be specified either o
      on Windows; instead you should use the --service command-line option.
      (Default: 0)
  
 +**LogTimeGranularity** __NUM__::
 +    Set the resolution of timestamps in Tor's logs to NUM milliseconds.
 +    NUM must be positive and either a divisor or a multiple of 1 second.
 +    Note that this option only controls the granularity written by Tor to
 +    a file or console log.  Tor does not (for example) "batch up" log
 +    messages to affect times logged by a controller, times attached to
 +    syslog messages, or the mtime fields on log files.  (Default: 1 second)
  
  **SafeLogging** **0**|**1**|**relay**::
      Tor can scrub potentially sensitive strings from log messages (e.g.
@@@ -404,11 -397,6 +404,11 @@@
      networkstatus. This is an advanced option; you generally shouldn't have
      to mess with it. (Default: not set.)
  
 +**DisableIOCP** **0**|**1**::
 +    If Tor was built to use the Libevent's "bufferevents" networking code
 +    and you're running on Windows, setting this option to 1 will tell Libevent
 +    not to use the Windows IOCP networking API.  (Default: 1)
 +
  CLIENT OPTIONS
  --------------
  
@@@ -483,7 -471,7 +483,7 @@@ The following options are useful only f
      list.
  
  **EntryNodes** __node__,__node__,__...__::
 -    A list of identity fingerprints, nicknames and address
 +    A list of identity fingerprints, nicknames, country codes and address
      patterns of nodes to use for the first hop in normal circuits. These are
      treated only as preferences unless StrictNodes (see below) is also set.
  
@@@ -580,8 -568,7 +580,8 @@@
      constitute a "family" of similar or co-administered servers, so never use
      any two of them in the same circuit. Defining a NodeFamily is only needed
      when a server doesn't list the family itself (with MyFamily). This option
 -    can be used multiple times.
 +    can be used multiple times.  In addition to nodes, you can also list
 +    IP address and ranges and country codes in {curly braces}.
  
  **EnforceDistinctSubnets** **0**|**1**::
      If 1, Tor will not put two servers whose IP addresses are "too close" on
@@@ -667,8 -654,9 +667,9 @@@
      can leak your location to attackers. (Default: 1)
  
  **VirtualAddrNetwork** __Address__/__bits__::
-     When a controller asks for a virtual (unused) address with the MAPADDRESS
-     command, Tor picks an unassigned address from this range. (Default:
+     When Tor needs to assign a virtual (unused) address because of a MAPADDRESS
+     command from the controller or the AutpmapHostsOnResolve feature, Tor
+     picks an unassigned address from this range. (Default:
      127.192.0.0/10) +
   +
      When providing proxy server service to a network of computers using a tool
@@@ -744,6 -732,12 +745,12 @@@
      192.168.0.1). This option prevents certain browser-based attacks; don't
      turn it off unless you know what you're doing. (Default: 1).
  
+ **ClientRejectInternalAddresses** **0**|**1**::
+     If true, Tor does not try to fulfill requests to connect to an internal
+     address (like 127.0.0.1 or 192.168.0.1) __unless a exit node is
+     specifically requested__ (for example, via a .exit hostname, or a
+     controller request).  (Default: 1).
+ 
  **DownloadExtraInfo** **0**|**1**::
      If true, Tor downloads and caches "extra-info" documents. These documents
      contain information about servers other than the information in their
@@@ -877,9 -871,7 +884,9 @@@ is non-zero)
      characters inclusive, and must contain only the characters [a-zA-Z0-9].
  
  **NumCPUs** __num__::
 -    How many processes to use at once for decrypting onionskins. (Default: 1)
 +    How many processes to use at once for decrypting onionskins and other
 +    parallelizable operations.  If this is set to 0, Tor will try to detect
 +    how many CPUs you have, defaulting to 1 if it can't tell.  (Default: 0)
  
  **ORPort** __PORT__::
      Advertise this port to listen for connections from Tor clients and servers.
@@@ -890,18 -882,6 +897,18 @@@
      specified in ORPort. (Default: 0.0.0.0) This directive can be specified
      multiple times to bind to multiple addresses/ports.
  
 +**PortForwarding** **0**|**1**::
 +    Attempt to automatically forward the DirPort and ORPort on a NAT router
 +    connecting this Tor server to the Internet. If set, Tor will try both
 +    NAT-PMP (common on Apple routers) and UPnP (common on routers from other
 +    manufacturers). (Default: 0)
 +
 +**PortForwardingHelper** __filename__|__pathname__::
 +    If PortForwarding is set, use this executable to configure the forwarding.
 +    If set to a filename, the system path will be searched for the executable.
 +    If set to a path, only the specified path will be executed.
 +    (Default: tor-fw-helper)
 +
  **PublishServerDescriptor** **0**|**1**|**v1**|**v2**|**v3**|**bridge**|**hidserv**,**...**::
      This option specifies which descriptors Tor will publish when acting as
      a relay or hidden service. You can
@@@ -1029,10 -1009,6 +1036,10 @@@
      When this option is enabled, Tor writes statistics on the number of relayed
      bytes and opened stream per exit port to disk every 24 hours. (Default: 0)
  
 +**ConnDirectionStatistics** **0**|**1**::
 +    When this option is enabled, Tor writes statistics on the bidirectional use
 +    of connections to disk every 24 hours. (Default: 0)
 +
  **ExtraInfoStatistics** **0**|**1**::
      When this option is enabled, Tor includes previously gathered statistics in
      its extra-info documents that it uploads to the directory authorities.
@@@ -1300,6 -1276,7 +1307,7 @@@ The following options are used for runn
         AuthDirMaxServersPerAddr 0
         AuthDirMaxServersPerAuthAddr 0
         ClientDNSRejectInternalAddresses 0
+        ClientRejectInternalAddresses 0
         ExitPolicyRejectPrivate 0
         V3AuthVotingInterval 5 minutes
         V3AuthVoteDelay 20 seconds
diff --combined src/or/config.c
index 1fa8466,5aca225..087049c
--- a/src/or/config.c
+++ b/src/or/config.c
@@@ -44,8 -44,6 +44,8 @@@ typedef enum config_type_t 
    CONFIG_TYPE_FILENAME,     /**< A filename: some prefixes get expanded. */
    CONFIG_TYPE_UINT,         /**< A non-negative integer less than MAX_INT */
    CONFIG_TYPE_INTERVAL,     /**< A number of seconds, with optional units*/
 +  CONFIG_TYPE_MSEC_INTERVAL,/**< A number of milliseconds, with optional
 +                              * units */
    CONFIG_TYPE_MEMUNIT,      /**< A number of bytes, with optional units*/
    CONFIG_TYPE_DOUBLE,       /**< A floating-point value */
    CONFIG_TYPE_BOOL,         /**< A boolean value, expressed as 0 or 1. */
@@@ -197,10 -195,10 +197,11 @@@ static config_var_t _option_vars[] = 
    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(ConnDirectionStatistics,     BOOL,     "0"),
    V(ConstrainedSockets,          BOOL,     "0"),
    V(ConstrainedSockSize,         MEMUNIT,  "8192"),
    V(ContactInfo,                 STRING,   NULL),
@@@ -224,10 -222,9 +225,10 @@@
    OBSOLETE("DirRecordUsageGranularity"),
    OBSOLETE("DirRecordUsageRetainIPs"),
    OBSOLETE("DirRecordUsageSaveInterval"),
 -  V(DirReqStatistics,            BOOL,     "0"),
 +  V(DirReqStatistics,            BOOL,     "1"),
    VAR("DirServer",               LINELIST, DirServers, NULL),
    V(DisableAllSwap,              BOOL,     "0"),
 +  V(DisableIOCP,                 BOOL,     "1"),
    V(DNSPort,                     UINT,     "0"),
    V(DNSListenAddress,            LINELIST, NULL),
    V(DownloadExtraInfo,           BOOL,     "0"),
@@@ -242,7 -239,7 +243,7 @@@
    V(ExitPolicy,                  LINELIST, NULL),
    V(ExitPolicyRejectPrivate,     BOOL,     "1"),
    V(ExitPortStatistics,          BOOL,     "0"),
 -  V(ExtraInfoStatistics,         BOOL,     "0"),
 +  V(ExtraInfoStatistics,         BOOL,     "1"),
  
  #if defined (WINCE)
    V(FallbackNetworkstatusFile,   FILENAME, "fallback-consensus"),
@@@ -294,7 -291,6 +295,7 @@@
    OBSOLETE("LinkPadding"),
    OBSOLETE("LogLevel"),
    OBSOLETE("LogFile"),
 +  V(LogTimeGranularity,          MSEC_INTERVAL, "1 second"),
    V(LongLivedPorts,              CSV,
                           "21,22,706,1863,5050,5190,5222,5223,6667,6697,8300"),
    VAR("MapAddress",              LINELIST, AddressMap,           NULL),
@@@ -311,7 -307,7 +312,7 @@@
    V(WarnUnsafeSocks,              BOOL,     "1"),
    OBSOLETE("NoPublish"),
    VAR("NodeFamily",              LINELIST, NodeFamilies,         NULL),
 -  V(NumCPUs,                     UINT,     "1"),
 +  V(NumCPUs,                     UINT,     "0"),
    V(NumEntryGuards,              UINT,     "3"),
    V(ORListenAddress,             LINELIST, NULL),
    V(ORPort,                      UINT,     "0"),
@@@ -321,8 -317,6 +322,8 @@@
    V(PerConnBWRate,               MEMUNIT,  "0"),
    V(PidFile,                     STRING,   NULL),
    V(TestingTorNetwork,           BOOL,     "0"),
 +  V(PortForwarding,              BOOL,     "0"),
 +  V(PortForwardingHelper,        FILENAME, "tor-fw-helper"),
    V(PreferTunneledDirConns,      BOOL,     "1"),
    V(ProtocolWarnings,            BOOL,     "0"),
    V(PublishServerDescriptor,     CSV,      "1"),
@@@ -391,7 -385,6 +392,7 @@@
    VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"),
    V(VirtualAddrNetwork,          STRING,   "127.192.0.0/10"),
    V(WarnPlaintextPorts,          CSV,      "23,109,110,143"),
 +  V(_UseFilteringSSLBufferevents, BOOL,    "0"),
    VAR("__ReloadTorrcOnSIGHUP",   BOOL,  ReloadTorrcOnSIGHUP,      "1"),
    VAR("__AllDirActionsPrivate",  BOOL,  AllDirActionsPrivate,     "0"),
    VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"),
@@@ -413,6 -406,7 +414,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"),
@@@ -563,9 -557,8 +565,9 @@@ static int is_listening_on_low_port(uin
                                      const config_line_t *listen_options);
  
  static uint64_t config_parse_memunit(const char *s, int *ok);
 +static int config_parse_msec_interval(const char *s, int *ok);
  static int config_parse_interval(const char *s, int *ok);
 -static void init_libevent(void);
 +static void init_libevent(const or_options_t *options);
  static int opt_streq(const char *s1, const char *s2);
  
  /** Magic value for or_options_t. */
@@@ -699,11 -692,6 +701,11 @@@ or_options_free(or_options_t *options
      return;
  
    routerset_free(options->_ExcludeExitNodesUnion);
 +  if (options->NodeFamilySets) {
 +    SMARTLIST_FOREACH(options->NodeFamilySets, routerset_t *,
 +                      rs, routerset_free(rs));
 +    smartlist_free(options->NodeFamilySets);
 +  }
    config_free(&options_format, options);
  }
  
@@@ -971,7 -959,7 +973,7 @@@ options_act_reversible(or_options_t *ol
      /* Set up libevent.  (We need to do this before we can register the
       * listeners as listeners.) */
      if (running_tor && !libevent_initialized) {
 -      init_libevent();
 +      init_libevent(options);
        libevent_initialized = 1;
      }
  
@@@ -1247,17 -1235,6 +1249,17 @@@ options_act(or_options_t *old_options
    if (accounting_is_enabled(options))
      configure_accounting(time(NULL));
  
 +#ifdef USE_BUFFEREVENTS
 +  /* If we're using the bufferevents implementation and our rate limits
 +   * changed, we need to tell the rate-limiting system about it. */
 +  if (!old_options ||
 +      old_options->BandwidthRate != options->BandwidthRate ||
 +      old_options->BandwidthBurst != options->BandwidthBurst ||
 +      old_options->RelayBandwidthRate != options->RelayBandwidthRate ||
 +      old_options->RelayBandwidthBurst != options->RelayBandwidthBurst)
 +    connection_bucket_init();
 +#endif
 +
    /* parse RefuseUnknownExits tristate */
    if (!strcmp(options->RefuseUnknownExits, "0"))
      options->RefuseUnknownExits_ = 0;
@@@ -1369,52 -1346,44 +1371,52 @@@
      tor_free(actual_fname);
    }
  
 -  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) {
 +      options->EntryStatistics || options->ExitPortStatistics ||
 +      options->ConnDirectionStatistics) {
      time_t now = time(NULL);
 +    int print_notice = 0;
      if ((!old_options || !old_options->CellStatistics) &&
 -        options->CellStatistics)
 +        options->CellStatistics) {
        rep_hist_buffer_stats_init(now);
 +      print_notice = 1;
 +    }
      if ((!old_options || !old_options->DirReqStatistics) &&
 -        options->DirReqStatistics)
 -      geoip_dirreq_stats_init(now);
 +        options->DirReqStatistics) {
 +      if (geoip_is_loaded()) {
 +        geoip_dirreq_stats_init(now);
 +        print_notice = 1;
 +      } else {
 +        options->DirReqStatistics = 0;
 +        log_notice(LD_CONFIG, "Configured to measure directory request "
 +                              "statistics, but no GeoIP database found! "
 +                              "Please specify a GeoIP database using the "
 +                              "GeoIPFile option!");
 +      }
 +    }
      if ((!old_options || !old_options->EntryStatistics) &&
 -        options->EntryStatistics)
 -      geoip_entry_stats_init(now);
 +        options->EntryStatistics && !should_record_bridge_info(options)) {
 +      if (geoip_is_loaded()) {
 +        geoip_entry_stats_init(now);
 +        print_notice = 1;
 +      } else {
 +        options->EntryStatistics = 0;
 +        log_notice(LD_CONFIG, "Configured to measure entry node "
 +                              "statistics, but no GeoIP database found! "
 +                              "Please specify a GeoIP database using the "
 +                              "GeoIPFile option!");
 +      }
 +    }
      if ((!old_options || !old_options->ExitPortStatistics) &&
 -        options->ExitPortStatistics)
 +        options->ExitPortStatistics) {
        rep_hist_exit_stats_init(now);
 -    if (!old_options)
 +      print_notice = 1;
 +    }
 +    if ((!old_options || !old_options->ConnDirectionStatistics) &&
 +        options->ConnDirectionStatistics) {
 +      rep_hist_conn_stats_init(now);
 +    }
 +    if (print_notice)
        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.");
@@@ -1432,9 -1401,6 +1434,9 @@@
    if (old_options && old_options->ExitPortStatistics &&
        !options->ExitPortStatistics)
      rep_hist_exit_stats_term();
 +  if (old_options && old_options->ConnDirectionStatistics &&
 +      !options->ConnDirectionStatistics)
 +    rep_hist_conn_stats_term();
  
    /* Check if we need to parse and add the EntryNodes config option. */
    if (options->EntryNodes &&
@@@ -1740,18 -1706,6 +1742,18 @@@ config_assign_value(config_format_t *fm
      break;
    }
  
 +  case CONFIG_TYPE_MSEC_INTERVAL: {
 +    i = config_parse_msec_interval(c->value, &ok);
 +    if (!ok) {
 +      tor_asprintf(msg,
 +          "Msec interval '%s %s' is malformed or out of bounds.",
 +          c->key, c->value);
 +      return -1;
 +    }
 +    *(int *)lvalue = i;
 +    break;
 +  }
 +
    case CONFIG_TYPE_MEMUNIT: {
      uint64_t u64 = config_parse_memunit(c->value, &ok);
      if (!ok) {
@@@ -2039,7 -1993,6 +2041,7 @@@ get_assigned_option(config_format_t *fm
        escape_val = 0; /* Can't need escape. */
        break;
      case CONFIG_TYPE_INTERVAL:
 +    case CONFIG_TYPE_MSEC_INTERVAL:
      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. */
@@@ -2267,7 -2220,6 +2269,7 @@@ option_clear(config_format_t *fmt, or_o
        *(time_t*)lvalue = 0;
        break;
      case CONFIG_TYPE_INTERVAL:
 +    case CONFIG_TYPE_MSEC_INTERVAL:
      case CONFIG_TYPE_UINT:
      case CONFIG_TYPE_BOOL:
        *(int*)lvalue = 0;
@@@ -2374,7 -2326,7 +2376,7 @@@ resolve_my_address(int warn_severity, o
    int explicit_ip=1;
    int explicit_hostname=1;
    int from_interface=0;
 -  char tmpbuf[INET_NTOA_BUF_LEN];
 +  char *addr_string = NULL;
    const char *address = options->Address;
    int notice_severity = warn_severity <= LOG_NOTICE ?
                            LOG_NOTICE : warn_severity;
@@@ -2416,43 -2368,48 +2418,43 @@@
          return -1;
        }
        from_interface = 1;
 -      in.s_addr = htonl(interface_ip);
 -      tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
 +      addr = interface_ip;
        log_fn(notice_severity, LD_CONFIG, "Learned IP address '%s' for "
 -             "local interface. Using that.", tmpbuf);
 +             "local interface. Using that.", fmt_addr32(addr));
        strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname));
      } else { /* resolved hostname into addr */
 -      in.s_addr = htonl(addr);
 -
        if (!explicit_hostname &&
 -          is_internal_IP(ntohl(in.s_addr), 0)) {
 +          is_internal_IP(addr, 0)) {
          uint32_t interface_ip;
  
 -        tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
          log_fn(notice_severity, LD_CONFIG, "Guessed local hostname '%s' "
 -               "resolves to a private IP address (%s).  Trying something "
 -               "else.", hostname, tmpbuf);
 +               "resolves to a private IP address (%s). Trying something "
 +               "else.", hostname, fmt_addr32(addr));
  
          if (get_interface_address(warn_severity, &interface_ip)) {
            log_fn(warn_severity, LD_CONFIG,
                   "Could not get local interface IP address. Too bad.");
          } else if (is_internal_IP(interface_ip, 0)) {
 -          struct in_addr in2;
 -          in2.s_addr = htonl(interface_ip);
 -          tor_inet_ntoa(&in2,tmpbuf,sizeof(tmpbuf));
            log_fn(notice_severity, LD_CONFIG,
                   "Interface IP address '%s' is a private address too. "
 -                 "Ignoring.", tmpbuf);
 +                 "Ignoring.", fmt_addr32(interface_ip));
          } else {
            from_interface = 1;
 -          in.s_addr = htonl(interface_ip);
 -          tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
 +          addr = interface_ip;
            log_fn(notice_severity, LD_CONFIG,
                   "Learned IP address '%s' for local interface."
 -                 " Using that.", tmpbuf);
 +                 " Using that.", fmt_addr32(addr));
            strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname));
          }
        }
      }
 +  } else {
 +    addr = ntohl(in.s_addr); /* set addr so that addr_string is not
 +                              * illformed */
    }
  
 -  tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
 -  if (is_internal_IP(ntohl(in.s_addr), 0)) {
 +  addr_string = tor_dup_ip(addr);
 +  if (is_internal_IP(addr, 0)) {
      /* make sure we're ok with publishing an internal IP */
      if (!options->DirServers && !options->AlternateDirAuthority) {
        /* if they are using the default dirservers, disallow internal IPs
@@@ -2460,8 -2417,7 +2462,8 @@@
        log_fn(warn_severity, LD_CONFIG,
               "Address '%s' resolves to private IP address '%s'. "
               "Tor servers that use the default DirServers must have public "
 -             "IP addresses.", hostname, tmpbuf);
 +             "IP addresses.", hostname, addr_string);
 +      tor_free(addr_string);
        return -1;
      }
      if (!explicit_ip) {
@@@ -2469,20 -2425,19 +2471,20 @@@
         * they're using an internal address. */
        log_fn(warn_severity, LD_CONFIG, "Address '%s' resolves to private "
               "IP address '%s'. Please set the Address config option to be "
 -             "the IP address you want to use.", hostname, tmpbuf);
 +             "the IP address you want to use.", hostname, addr_string);
 +      tor_free(addr_string);
        return -1;
      }
    }
  
 -  log_debug(LD_CONFIG, "Resolved Address to '%s'.", tmpbuf);
 -  *addr_out = ntohl(in.s_addr);
 +  log_debug(LD_CONFIG, "Resolved Address to '%s'.", fmt_addr32(addr));
 +  *addr_out = addr;
    if (last_resolved_addr && last_resolved_addr != *addr_out) {
      /* Leave this as a notice, regardless of the requested severity,
       * at least until dynamic IP address support becomes bulletproof. */
      log_notice(LD_NET,
                 "Your IP address seems to have changed to %s. Updating.",
 -               tmpbuf);
 +               addr_string);
      ip_address_changed(0);
    }
    if (last_resolved_addr != *addr_out) {
@@@ -2501,12 -2456,11 +2503,12 @@@
      }
      control_event_server_status(LOG_NOTICE,
                                  "EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s %s%s",
 -                                tmpbuf, method, h?"HOSTNAME=":"", h);
 +                                addr_string, method, h?"HOSTNAME=":"", h);
    }
    last_resolved_addr = *addr_out;
    if (hostname_out)
      *hostname_out = tor_strdup(hostname);
 +  tor_free(addr_string);
    return 0;
  }
  
@@@ -3111,24 -3065,17 +3113,24 @@@ options_validate(or_options_t *old_opti
      routerset_union(options->_ExcludeExitNodesUnion,options->ExcludeNodes);
    }
  
 +  if (options->NodeFamilies) {
 +    options->NodeFamilySets = smartlist_create();
 +    for (cl = options->NodeFamilies; cl; cl = cl->next) {
 +      routerset_t *rs = routerset_new();
 +      if (routerset_parse(rs, cl->value, cl->key) == 0) {
 +        smartlist_add(options->NodeFamilySets, rs);
 +      } else {
 +        routerset_free(rs);
 +      }
 +    }
 +  }
 +
    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(). */
 -    REJECT("IPs or countries are not yet supported in EntryNodes.");
 -  }
 -
    if (options->AuthoritativeDir) {
      if (!options->ContactInfo && !options->TestingTorNetwork)
        REJECT("Authoritative directory servers must set ContactInfo");
@@@ -3589,12 -3536,8 +3591,12 @@@
    if (check_nickname_list(options->MyFamily, "MyFamily", msg))
      return -1;
    for (cl = options->NodeFamilies; cl; cl = cl->next) {
 -    if (check_nickname_list(cl->value, "NodeFamily", msg))
 +    routerset_t *rs = routerset_new();
 +    if (routerset_parse(rs, cl->value, cl->key)) {
 +      routerset_free(rs);
        return -1;
 +    }
 +    routerset_free(rs);
    }
  
    if (validate_addr_policies(options, msg) < 0)
@@@ -4375,35 -4318,6 +4377,35 @@@ options_init_logs(or_options_t *options
                 options->RunAsDaemon;
  #endif
  
 +  if (options->LogTimeGranularity <= 0) {
 +    log_warn(LD_CONFIG, "Log time granularity '%d' has to be positive.",
 +             options->LogTimeGranularity);
 +    return -1;
 +  } else if (1000 % options->LogTimeGranularity != 0 &&
 +             options->LogTimeGranularity % 1000 != 0) {
 +    int granularity = options->LogTimeGranularity;
 +    if (granularity < 40) {
 +      do granularity++;
 +      while (1000 % granularity != 0);
 +    } else if (granularity < 1000) {
 +      granularity = 1000 / granularity;
 +      while (1000 % granularity != 0)
 +        granularity--;
 +      granularity = 1000 / granularity;
 +    } else {
 +      granularity = 1000 * ((granularity / 1000) + 1);
 +    }
 +    log_warn(LD_CONFIG, "Log time granularity '%d' has to be either a "
 +                        "divisor or a multiple of 1 second. Changing to "
 +                        "'%d'.",
 +             options->LogTimeGranularity, granularity);
 +    if (!validate_only)
 +      set_log_time_granularity(granularity);
 +  } else {
 +    if (!validate_only)
 +      set_log_time_granularity(options->LogTimeGranularity);
 +  }
 +
    ok = 1;
    elts = smartlist_create();
  
@@@ -4892,26 -4806,6 +4894,26 @@@ static struct unit_table_t time_units[
    { NULL, 0 },
  };
  
 +/** Table to map the names of time units to the number of milliseconds
 + * they contain. */
 +static struct unit_table_t time_msec_units[] = {
 +  { "",         1 },
 +  { "msec",     1 },
 +  { "millisecond", 1 },
 +  { "milliseconds", 1 },
 +  { "second",   1000 },
 +  { "seconds",  1000 },
 +  { "minute",   60*1000 },
 +  { "minutes",  60*1000 },
 +  { "hour",     60*60*1000 },
 +  { "hours",    60*60*1000 },
 +  { "day",      24*60*60*1000 },
 +  { "days",     24*60*60*1000 },
 +  { "week",     7*24*60*60*1000 },
 +  { "weeks",    7*24*60*60*1000 },
 +  { NULL, 0 },
 +};
 +
  /** Parse a string <b>val</b> containing a number, zero or more
   * spaces, and an optional unit string.  If the unit appears in the
   * table <b>u</b>, then multiply the number by the unit multiplier.
@@@ -4975,25 -4869,6 +4977,25 @@@ config_parse_memunit(const char *s, in
    return u;
  }
  
 +/** Parse a string in the format "number unit", where unit is a unit of
 + * time in milliseconds.  On success, set *<b>ok</b> to true and return
 + * the number of milliseconds in the provided interval.  Otherwise, set
 + * *<b>ok</b> to 0 and return -1. */
 +static int
 +config_parse_msec_interval(const char *s, int *ok)
 +{
 +  uint64_t r;
 +  r = config_parse_units(s, time_msec_units, ok);
 +  if (!ok)
 +    return -1;
 +  if (r > INT_MAX) {
 +    log_warn(LD_CONFIG, "Msec interval '%s' is too long", s);
 +    *ok = 0;
 +    return -1;
 +  }
 +  return (int)r;
 +}
 +
  /** Parse a string in the format "number unit", where unit is a unit of time.
   * On success, set *<b>ok</b> to true and return the number of seconds in
   * the provided interval.  Otherwise, set *<b>ok</b> to 0 and return -1.
@@@ -5013,29 -4888,13 +5015,29 @@@ config_parse_interval(const char *s, in
    return (int)r;
  }
  
 +/** Return the number of cpus configured in <b>options</b>.  If we are
 + * told to auto-detect the number of cpus, return the auto-detected number. */
 +int
 +get_num_cpus(const or_options_t *options)
 +{
 +  if (options->NumCPUs == 0) {
 +    int n = compute_num_cpus();
 +    return (n >= 1) ? n : 1;
 +  } else {
 +    return options->NumCPUs;
 +  }
 +}
 +
  /**
   * Initialize the libevent library.
   */
  static void
 -init_libevent(void)
 +init_libevent(const or_options_t *options)
  {
    const char *badness=NULL;
 +  tor_libevent_cfg cfg;
 +
 +  tor_assert(options);
  
    configure_libevent_logging();
    /* If the kernel complains that some method (say, epoll) doesn't
@@@ -5045,11 -4904,7 +5047,11 @@@
  
    tor_check_libevent_header_compatibility();
  
 -  tor_libevent_initialize();
 +  memset(&cfg, 0, sizeof(cfg));
 +  cfg.disable_iocp = options->DisableIOCP;
 +  cfg.num_cpus = get_num_cpus(options);
 +
 +  tor_libevent_initialize(&cfg);
  
    suppress_libevent_log_msg(NULL);
  
@@@ -5390,7 -5245,6 +5392,7 @@@ getinfo_helper_config(control_connectio
          case CONFIG_TYPE_FILENAME: type = "Filename"; break;
          case CONFIG_TYPE_UINT: type = "Integer"; break;
          case CONFIG_TYPE_INTERVAL: type = "TimeInterval"; break;
 +        case CONFIG_TYPE_MSEC_INTERVAL: type = "TimeMsecInterval"; break;
          case CONFIG_TYPE_MEMUNIT: type = "DataSize"; break;
          case CONFIG_TYPE_DOUBLE: type = "Float"; break;
          case CONFIG_TYPE_BOOL: type = "Boolean"; break;
diff --combined src/or/connection.c
index 6f2eeb2,fd30ac8..874e724
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@@ -36,10 -36,6 +36,10 @@@
  #include "router.h"
  #include "routerparse.h"
  
 +#ifdef USE_BUFFEREVENTS
 +#include <event2/event.h>
 +#endif
 +
  static connection_t *connection_create_listener(
                                 struct sockaddr *listensockaddr,
                                 socklen_t listensocklen, int type,
@@@ -49,10 -45,8 +49,10 @@@ static void connection_init(time_t now
  static int connection_init_accepted_conn(connection_t *conn,
                                           uint8_t listener_type);
  static int connection_handle_listener_read(connection_t *conn, int new_type);
 +#ifndef USE_BUFFEREVENTS
  static int connection_bucket_should_increase(int bucket,
                                               or_connection_t *conn);
 +#endif
  static int connection_finished_flushing(connection_t *conn);
  static int connection_flushed_some(connection_t *conn);
  static int connection_finished_connecting(connection_t *conn);
@@@ -189,26 -183,6 +189,26 @@@ conn_state_to_string(int type, int stat
    return buf;
  }
  
 +#ifdef USE_BUFFEREVENTS
 +/** Return true iff the connection's type is one that can use a
 +    bufferevent-based implementation. */
 +int
 +connection_type_uses_bufferevent(connection_t *conn)
 +{
 +  switch (conn->type) {
 +    case CONN_TYPE_AP:
 +    case CONN_TYPE_EXIT:
 +    case CONN_TYPE_DIR:
 +    case CONN_TYPE_CONTROL:
 +    case CONN_TYPE_OR:
 +    case CONN_TYPE_CPUWORKER:
 +      return 1;
 +    default:
 +      return 0;
 +  }
 +}
 +#endif
 +
  /** Allocate and return a new dir_connection_t, initialized as by
   * connection_init(). */
  dir_connection_t *
@@@ -334,13 -308,10 +334,13 @@@ connection_init(time_t now, connection_
  
    conn->type = type;
    conn->socket_family = socket_family;
 -  if (!connection_is_listener(conn)) { /* listeners never use their buf */
 +#ifndef USE_BUFFEREVENTS
 +  if (!connection_is_listener(conn)) {
 +    /* listeners never use their buf */
      conn->inbuf = buf_new();
      conn->outbuf = buf_new();
    }
 +#endif
  
    conn->timestamp_created = now;
    conn->timestamp_lastread = now;
@@@ -406,8 -377,7 +406,8 @@@ _connection_free(connection_t *conn
               "bytes on inbuf, %d on outbuf.",
               conn_type_to_string(conn->type),
               conn_state_to_string(conn->type, conn->state),
 -             (int)buf_datalen(conn->inbuf), (int)buf_datalen(conn->outbuf));
 +             (int)connection_get_inbuf_len(conn),
 +             (int)connection_get_outbuf_len(conn));
    }
  
    if (!connection_is_listener(conn)) {
@@@ -454,15 -424,6 +454,15 @@@
  
    tor_free(conn->read_event); /* Probably already freed by connection_free. */
    tor_free(conn->write_event); /* Probably already freed by connection_free. */
 +  IF_HAS_BUFFEREVENT(conn, {
 +      /* This was a workaround to handle bugs in some old versions of libevent
 +       * where callbacks can occur after calling bufferevent_free().  Setting
 +       * the callbacks to NULL prevented this.  It shouldn't be necessary any
 +       * more, but let's not tempt fate for now.  */
 +      bufferevent_setcb(conn->bufev, NULL, NULL, NULL, NULL);
 +      bufferevent_free(conn->bufev);
 +      conn->bufev = NULL;
 +  });
  
    if (conn->type == CONN_TYPE_DIR) {
      dir_connection_t *dir_conn = TO_DIR_CONN(conn);
@@@ -489,11 -450,6 +489,11 @@@
      log_warn(LD_BUG, "called on OR conn with non-zeroed identity_digest");
      connection_or_remove_from_identity_map(TO_OR_CONN(conn));
    }
 +#ifdef USE_BUFFEREVENTS
 +  if (conn->type == CONN_TYPE_OR && TO_OR_CONN(conn)->bucket_cfg) {
 +    ev_token_bucket_cfg_free(TO_OR_CONN(conn)->bucket_cfg);
 +  }
 +#endif
  
    memset(mem, 0xCC, memlen); /* poison memory */
    tor_free(mem);
@@@ -719,9 -675,10 +719,9 @@@ connection_close_immediate(connection_
    conn->s = -1;
    if (conn->linked)
      conn->linked_conn_is_closed = 1;
 -  if (!connection_is_listener(conn)) {
 +  if (conn->outbuf)
      buf_clear(conn->outbuf);
 -    conn->outbuf_flushlen = 0;
 -  }
 +  conn->outbuf_flushlen = 0;
  }
  
  /** Mark <b>conn</b> to be closed next time we loop through
@@@ -1221,7 -1178,8 +1221,8 @@@ connection_handle_listener_read(connect
    }
  
    if (connection_init_accepted_conn(newconn, conn->type) < 0) {
-     connection_mark_for_close(newconn);
+     if (! conn->marked_for_close)
+       connection_mark_for_close(newconn);
      return 0;
    }
    return 0;
@@@ -1247,9 -1205,11 +1248,11 @@@ connection_init_accepted_conn(connectio
            conn->state = AP_CONN_STATE_SOCKS_WAIT;
            break;
          case CONN_TYPE_AP_TRANS_LISTENER:
+           TO_EDGE_CONN(conn)->is_transparent_ap = 1;
            conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
            return connection_ap_process_transparent(TO_EDGE_CONN(conn));
          case CONN_TYPE_AP_NATD_LISTENER:
+           TO_EDGE_CONN(conn)->is_transparent_ap = 1;
            conn->state = AP_CONN_STATE_NATD_WAIT;
            break;
        }
@@@ -1281,7 -1241,7 +1284,7 @@@ connection_connect(connection_t *conn, 
    int s, inprogress = 0;
    char addrbuf[256];
    struct sockaddr *dest_addr = (struct sockaddr*) addrbuf;
 -  socklen_t dest_addr_len;
 +  int dest_addr_len;
    or_options_t *options = get_options();
    int protocol_family;
  
@@@ -1337,7 -1297,7 +1340,7 @@@
    log_debug(LD_NET, "Connecting to %s:%u.",
              escaped_safe_str_client(address), port);
  
 -  if (connect(s, dest_addr, dest_addr_len) < 0) {
 +  if (connect(s, dest_addr, (socklen_t)dest_addr_len) < 0) {
      int e = tor_socket_errno(s);
      if (!ERRNO_IS_CONN_EINPROGRESS(e)) {
        /* yuck. kill it. */
@@@ -1362,7 -1322,7 +1365,7 @@@
           escaped_safe_str_client(address),
           port, inprogress?"in progress":"established", s);
    conn->s = s;
 -  if (connection_add(conn) < 0) /* no space, forget it */
 +  if (connection_add_connecting(conn) < 0) /* no space, forget it */
      return -1;
    return inprogress ? 0 : 1;
  }
@@@ -1585,19 -1545,6 +1588,19 @@@ connection_send_socks5_connect(connecti
    conn->proxy_state = PROXY_SOCKS5_WANT_CONNECT_OK;
  }
  
 +/** DOCDOC */
 +static int
 +connection_fetch_from_buf_socks_client(connection_t *conn,
 +                                       int state, char **reason)
 +{
 +  IF_HAS_BUFFEREVENT(conn, {
 +    struct evbuffer *input = bufferevent_get_input(conn->bufev);
 +    return fetch_from_evbuffer_socks_client(input, state, reason);
 +  }) ELSE_IF_NO_BUFFEREVENT {
 +    return fetch_from_buf_socks_client(conn->inbuf, state, reason);
 +  }
 +}
 +
  /** Call this from connection_*_process_inbuf() to advance the proxy
   * handshake.
   *
@@@ -1625,17 -1572,17 +1628,17 @@@ connection_read_proxy_handshake(connect
        break;
  
      case PROXY_SOCKS4_WANT_CONNECT_OK:
 -      ret = fetch_from_buf_socks_client(conn->inbuf,
 -                                        conn->proxy_state,
 -                                        &reason);
 +      ret = connection_fetch_from_buf_socks_client(conn,
 +                                                   conn->proxy_state,
 +                                                   &reason);
        if (ret == 1)
          conn->proxy_state = PROXY_CONNECTED;
        break;
  
      case PROXY_SOCKS5_WANT_AUTH_METHOD_NONE:
 -      ret = fetch_from_buf_socks_client(conn->inbuf,
 -                                        conn->proxy_state,
 -                                        &reason);
 +      ret = connection_fetch_from_buf_socks_client(conn,
 +                                                   conn->proxy_state,
 +                                                   &reason);
        /* no auth needed, do connect */
        if (ret == 1) {
          connection_send_socks5_connect(conn);
@@@ -1644,9 -1591,9 +1647,9 @@@
        break;
  
      case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929:
 -      ret = fetch_from_buf_socks_client(conn->inbuf,
 -                                        conn->proxy_state,
 -                                        &reason);
 +      ret = connection_fetch_from_buf_socks_client(conn,
 +                                                   conn->proxy_state,
 +                                                   &reason);
  
        /* send auth if needed, otherwise do connect */
        if (ret == 1) {
@@@ -1681,9 -1628,9 +1684,9 @@@
        break;
  
      case PROXY_SOCKS5_WANT_AUTH_RFC1929_OK:
 -      ret = fetch_from_buf_socks_client(conn->inbuf,
 -                                        conn->proxy_state,
 -                                        &reason);
 +      ret = connection_fetch_from_buf_socks_client(conn,
 +                                                   conn->proxy_state,
 +                                                   &reason);
        /* send the connect request */
        if (ret == 1) {
          connection_send_socks5_connect(conn);
@@@ -1692,9 -1639,9 +1695,9 @@@
        break;
  
      case PROXY_SOCKS5_WANT_CONNECT_OK:
 -      ret = fetch_from_buf_socks_client(conn->inbuf,
 -                                        conn->proxy_state,
 -                                        &reason);
 +      ret = connection_fetch_from_buf_socks_client(conn,
 +                                                   conn->proxy_state,
 +                                                   &reason);
        if (ret == 1)
          conn->proxy_state = PROXY_CONNECTED;
        break;
@@@ -1958,9 -1905,6 +1961,9 @@@ connection_is_rate_limited(connection_
      return 1;
  }
  
 +#ifdef USE_BUFFEREVENTS
 +static struct bufferevent_rate_limit_group *global_rate_limit = NULL;
 +#else
  extern int global_read_bucket, global_write_bucket;
  extern int global_relayed_read_bucket, global_relayed_write_bucket;
  
@@@ -1968,13 -1912,11 +1971,13 @@@
   * we are likely to run dry again this second, so be stingy with the
   * tokens we just put in. */
  static int write_buckets_empty_last_second = 0;
 +#endif
  
  /** How many seconds of no active local circuits will make the
   * connection revert to the "relayed" bandwidth class? */
  #define CLIENT_IDLE_TIME_FOR_PRIORITY 30
  
 +#ifndef USE_BUFFEREVENTS
  /** Return 1 if <b>conn</b> should use tokens from the "relayed"
   * bandwidth rates, else 0. Currently, only OR conns with bandwidth
   * class 1, and directory conns that are serving data out, count.
@@@ -2085,20 -2027,6 +2088,20 @@@ connection_bucket_write_limit(connectio
    return connection_bucket_round_robin(base, priority,
                                         global_bucket, conn_bucket);
  }
 +#else
 +static ssize_t
 +connection_bucket_read_limit(connection_t *conn, time_t now)
 +{
 +  (void) now;
 +  return bufferevent_get_max_to_read(conn->bufev);
 +}
 +ssize_t
 +connection_bucket_write_limit(connection_t *conn, time_t now)
 +{
 +  (void) now;
 +  return bufferevent_get_max_to_write(conn->bufev);
 +}
 +#endif
  
  /** Return 1 if the global write buckets are low enough that we
   * shouldn't send <b>attempt</b> bytes of low-priority directory stuff
@@@ -2123,12 -2051,8 +2126,12 @@@
  int
  global_write_bucket_low(connection_t *conn, size_t attempt, int priority)
  {
 +#ifdef USE_BUFFEREVENTS
 +  ssize_t smaller_bucket = bufferevent_get_max_to_write(conn->bufev);
 +#else
    int smaller_bucket = global_write_bucket < global_relayed_write_bucket ?
                         global_write_bucket : global_relayed_write_bucket;
 +#endif
    if (authdir_mode(get_options()) && priority>1)
      return 0; /* there's always room to answer v2 if we're an auth dir */
  
@@@ -2138,10 -2062,8 +2141,10 @@@
    if (smaller_bucket < (int)attempt)
      return 1; /* not enough space no matter the priority */
  
 +#ifndef USE_BUFFEREVENTS
    if (write_buckets_empty_last_second)
      return 1; /* we're already hitting our limits, no more please */
 +#endif
  
    if (priority == 1) { /* old-style v1 query */
      /* Could we handle *two* of these requests within the next two seconds? */
@@@ -2157,7 -2079,6 +2160,7 @@@
    return 0;
  }
  
 +#ifndef USE_BUFFEREVENTS
  /** We just read <b>num_read</b> and wrote <b>num_written</b> bytes
   * onto <b>conn</b>. Decrement buckets appropriately. */
  static void
@@@ -2185,11 -2106,6 +2188,11 @@@ connection_buckets_decrement(connection
  
    if (!connection_is_rate_limited(conn))
      return; /* local IPs are free */
 +
 +  if (conn->type == CONN_TYPE_OR)
 +    rep_hist_note_or_conn_bytes(conn->global_identifier, num_read,
 +                                num_written, now);
 +
    if (num_read > 0) {
      rep_hist_note_bytes_read(num_read, now);
    }
@@@ -2406,88 -2322,6 +2409,88 @@@ connection_bucket_should_increase(int b
  
    return 1;
  }
 +#else
 +
 +static void
 +connection_buckets_decrement(connection_t *conn, time_t now,
 +                             size_t num_read, size_t num_written)
 +{
 +  (void) conn;
 +  (void) now;
 +  (void) num_read;
 +  (void) num_written;
 +  /* Libevent does this for us. */
 +}
 +void
 +connection_bucket_refill(int seconds_elapsed, time_t now)
 +{
 +  (void) seconds_elapsed;
 +  (void) now;
 +  /* Libevent does this for us. */
 +}
 +void
 +connection_bucket_init(void)
 +{
 +  or_options_t *options = get_options();
 +  const struct timeval *tick = tor_libevent_get_one_tick_timeout();
 +  struct ev_token_bucket_cfg *bucket_cfg;
 +
 +  uint64_t rate, burst;
 +  if (options->RelayBandwidthRate) {
 +    rate = options->RelayBandwidthRate;
 +    burst = options->RelayBandwidthBurst;
 +  } else {
 +    rate = options->BandwidthRate;
 +    burst = options->BandwidthBurst;
 +  }
 +
 +  rate /= TOR_LIBEVENT_TICKS_PER_SECOND;
 +  bucket_cfg = ev_token_bucket_cfg_new((uint32_t)rate, (uint32_t)burst,
 +                                       (uint32_t)rate, (uint32_t)burst,
 +                                       tick);
 +
 +  if (!global_rate_limit) {
 +    global_rate_limit =
 +      bufferevent_rate_limit_group_new(tor_libevent_get_base(), bucket_cfg);
 +  } else {
 +    bufferevent_rate_limit_group_set_cfg(global_rate_limit, bucket_cfg);
 +  }
 +  ev_token_bucket_cfg_free(bucket_cfg);
 +}
 +
 +void
 +connection_get_rate_limit_totals(uint64_t *read_out, uint64_t *written_out)
 +{
 +  if (global_rate_limit == NULL) {
 +    *read_out = *written_out = 0;
 +  } else {
 +    bufferevent_rate_limit_group_get_totals(
 +      global_rate_limit, read_out, written_out);
 +  }
 +}
 +
 +/** DOCDOC */
 +void
 +connection_enable_rate_limiting(connection_t *conn)
 +{
 +  if (conn->bufev) {
 +    if (!global_rate_limit)
 +      connection_bucket_init();
 +    bufferevent_add_to_rate_limit_group(conn->bufev, global_rate_limit);
 +  }
 +}
 +
 +static void
 +connection_consider_empty_write_buckets(connection_t *conn)
 +{
 +  (void) conn;
 +}
 +static void
 +connection_consider_empty_read_buckets(connection_t *conn)
 +{
 +  (void) conn;
 +}
 +#endif
  
  /** Read bytes from conn-\>s and process them.
   *
@@@ -2741,13 -2575,13 +2744,13 @@@ connection_read_to_buf(connection_t *co
    }
  
    if (n_read > 0) { /* change *max_to_read */
 -    /*XXXX021 check for overflow*/
 +    /*XXXX022 check for overflow*/
      *max_to_read = (int)(at_most - n_read);
    }
  
    if (conn->type == CONN_TYPE_AP) {
      edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
 -    /*XXXX021 check for overflow*/
 +    /*XXXX022 check for overflow*/
      edge_conn->n_read += (int)n_read;
    }
  
@@@ -2769,202 -2603,11 +2772,202 @@@
    return 0;
  }
  
 +#ifdef USE_BUFFEREVENTS
 +/* XXXX These generic versions could be simplified by making them
 +   type-specific */
 +
 +/** Callback: Invoked whenever bytes are added to or drained from an input
 + * evbuffer.  Used to track the number of bytes read. */
 +static void
 +evbuffer_inbuf_callback(struct evbuffer *buf,
 +                        const struct evbuffer_cb_info *info, void *arg)
 +{
 +  connection_t *conn = arg;
 +  (void) buf;
 +  /* XXXX These need to get real counts on the non-nested TLS case. - NM */
 +  if (info->n_added) {
 +    time_t now = approx_time();
 +    conn->timestamp_lastread = now;
 +    connection_buckets_decrement(conn, now, info->n_added, 0);
 +    connection_consider_empty_read_buckets(conn);
 +    if (conn->type == CONN_TYPE_AP) {
 +      edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
 +      /*XXXX022 check for overflow*/
 +      edge_conn->n_read += (int)info->n_added;
 +    }
 +  }
 +}
 +
 +/** Callback: Invoked whenever bytes are added to or drained from an output
 + * evbuffer.  Used to track the number of bytes written. */
 +static void
 +evbuffer_outbuf_callback(struct evbuffer *buf,
 +                         const struct evbuffer_cb_info *info, void *arg)
 +{
 +  connection_t *conn = arg;
 +  (void)buf;
 +  if (info->n_deleted) {
 +    time_t now = approx_time();
 +    conn->timestamp_lastwritten = now;
 +    connection_buckets_decrement(conn, now, 0, info->n_deleted);
 +    connection_consider_empty_write_buckets(conn);
 +    if (conn->type == CONN_TYPE_AP) {
 +      edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
 +      /*XXXX022 check for overflow*/
 +      edge_conn->n_written += (int)info->n_deleted;
 +    }
 +  }
 +}
 +
 +/** Callback: invoked whenever a bufferevent has read data. */
 +void
 +connection_handle_read_cb(struct bufferevent *bufev, void *arg)
 +{
 +  connection_t *conn = arg;
 +  (void) bufev;
 +  if (!conn->marked_for_close)
 +    if (connection_process_inbuf(conn, 1)<0) /* XXXX Always 1? */
 +      connection_mark_for_close(conn);
 +}
 +
 +/** Callback: invoked whenever a bufferevent has written data. */
 +void
 +connection_handle_write_cb(struct bufferevent *bufev, void *arg)
 +{
 +  connection_t *conn = arg;
 +  struct evbuffer *output;
 +  if (connection_flushed_some(conn)<0) {
 +    connection_mark_for_close(conn);
 +    return;
 +  }
 +
 +  output = bufferevent_get_output(bufev);
 +  if (!evbuffer_get_length(output)) {
 +    connection_finished_flushing(conn);
 +    if (conn->marked_for_close && conn->hold_open_until_flushed) {
 +      conn->hold_open_until_flushed = 0;
 +      if (conn->linked) {
 +        /* send eof */
 +        bufferevent_flush(conn->bufev, EV_WRITE, BEV_FINISHED);
 +      }
 +    }
 +  }
 +}
 +
 +/** Callback: invoked whenever a bufferevent has had an event (like a
 + * connection, or an eof, or an error) occur. */
 +void
 +connection_handle_event_cb(struct bufferevent *bufev, short event, void *arg)
 +{
 +  connection_t *conn = arg;
 +  (void) bufev;
 +  if (event & BEV_EVENT_CONNECTED) {
 +    tor_assert(connection_state_is_connecting(conn));
 +    if (connection_finished_connecting(conn)<0)
 +      return;
 +  }
 +  if (event & BEV_EVENT_EOF) {
 +    if (!conn->marked_for_close) {
 +      conn->inbuf_reached_eof = 1;
 +      if (connection_reached_eof(conn)<0)
 +        return;
 +    }
 +  }
 +  if (event & BEV_EVENT_ERROR) {
 +    int socket_error = evutil_socket_geterror(conn->s);
 +    if (conn->type == CONN_TYPE_OR &&
 +        conn->state == OR_CONN_STATE_CONNECTING) {
 +      connection_or_connect_failed(TO_OR_CONN(conn),
 +                                   errno_to_orconn_end_reason(socket_error),
 +                                   tor_socket_strerror(socket_error));
 +    } else if (CONN_IS_EDGE(conn)) {
 +      edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
 +      if (!edge_conn->edge_has_sent_end)
 +        connection_edge_end_errno(edge_conn);
 +      if (edge_conn->socks_request) /* broken, don't send a socks reply back */
 +        edge_conn->socks_request->has_finished = 1;
 +    }
 +    connection_close_immediate(conn); /* Connection is dead. */
 +    if (!conn->marked_for_close)
 +      connection_mark_for_close(conn);
 +  }
 +}
 +
 +/** Set up the generic callbacks for the bufferevent on <b>conn</b>. */
 +void
 +connection_configure_bufferevent_callbacks(connection_t *conn)
 +{
 +  struct bufferevent *bufev;
 +  struct evbuffer *input, *output;
 +  tor_assert(conn->bufev);
 +  bufev = conn->bufev;
 +  bufferevent_setcb(bufev,
 +                    connection_handle_read_cb,
 +                    connection_handle_write_cb,
 +                    connection_handle_event_cb,
 +                    conn);
 +
 +  input = bufferevent_get_input(bufev);
 +  output = bufferevent_get_output(bufev);
 +  evbuffer_add_cb(input, evbuffer_inbuf_callback, conn);
 +  evbuffer_add_cb(output, evbuffer_outbuf_callback, conn);
 +}
 +#endif
 +
  /** A pass-through to fetch_from_buf. */
  int
  connection_fetch_from_buf(char *string, size_t len, connection_t *conn)
  {
 -  return fetch_from_buf(string, len, conn->inbuf);
 +  IF_HAS_BUFFEREVENT(conn, {
 +    /* XXX overflow -seb */
 +    return (int)bufferevent_read(conn->bufev, string, len);
 +  }) ELSE_IF_NO_BUFFEREVENT {
 +    return fetch_from_buf(string, len, conn->inbuf);
 +  }
 +}
 +
 +/** As fetch_from_buf_line(), but read from a connection's input buffer. */
 +int
 +connection_fetch_from_buf_line(connection_t *conn, char *data,
 +                               size_t *data_len)
 +{
 +  IF_HAS_BUFFEREVENT(conn, {
 +    int r;
 +    size_t eol_len=0;
 +    struct evbuffer *input = bufferevent_get_input(conn->bufev);
 +    struct evbuffer_ptr ptr =
 +      evbuffer_search_eol(input, NULL, &eol_len, EVBUFFER_EOL_LF);
 +    if (ptr.pos == -1)
 +      return 0; /* No EOL found. */
 +    if ((size_t)ptr.pos+eol_len >= *data_len) {
 +      return -1; /* Too long */
 +    }
 +    *data_len = ptr.pos+eol_len;
 +    r = evbuffer_remove(input, data, ptr.pos+eol_len);
 +    tor_assert(r >= 0);
 +    data[ptr.pos+eol_len] = '\0';
 +    return 1;
 +  }) ELSE_IF_NO_BUFFEREVENT {
 +    return fetch_from_buf_line(conn->inbuf, data, data_len);
 +  }
 +}
 +
 +/** As fetch_from_buf_http, but fetches from a conncetion's input buffer_t or
 + * its bufferevent as appropriate. */
 +int
 +connection_fetch_from_buf_http(connection_t *conn,
 +                               char **headers_out, size_t max_headerlen,
 +                               char **body_out, size_t *body_used,
 +                               size_t max_bodylen, int force_complete)
 +{
 +  IF_HAS_BUFFEREVENT(conn, {
 +    struct evbuffer *input = bufferevent_get_input(conn->bufev);
 +    return fetch_from_evbuffer_http(input, headers_out, max_headerlen,
 +                            body_out, body_used, max_bodylen, force_complete);
 +  }) ELSE_IF_NO_BUFFEREVENT {
 +    return fetch_from_buf_http(conn->inbuf, headers_out, max_headerlen,
 +                            body_out, body_used, max_bodylen, force_complete);
 +  }
  }
  
  /** Return conn-\>outbuf_flushlen: how many bytes conn wants to flush
@@@ -3085,7 -2728,6 +3088,7 @@@ connection_handle_write_impl(connection
  
      /* If we just flushed the last bytes, check if this tunneled dir
       * request is done. */
 +    /* XXXX move this to flushed_some or finished_flushing -NM */
      if (buf_datalen(conn->outbuf) == 0 && conn->dirreq_id)
        geoip_change_dirreq_state(conn->dirreq_id, DIRREQ_TUNNELED,
                                  DIRREQ_OR_CONN_BUFFER_FLUSHED);
@@@ -3141,7 -2783,7 +3144,7 @@@
  
    if (conn->type == CONN_TYPE_AP) {
      edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
 -    /*XXXX021 check for overflow.*/
 +    /*XXXX022 check for overflow.*/
      edge_conn->n_written += (int)n_written;
    }
  
@@@ -3214,22 -2856,6 +3217,22 @@@ _connection_write_to_buf_impl(const cha
    if (conn->marked_for_close && !conn->hold_open_until_flushed)
      return;
  
 +  IF_HAS_BUFFEREVENT(conn, {
 +    if (zlib) {
 +      int done = zlib < 0;
 +      r = write_to_evbuffer_zlib(bufferevent_get_output(conn->bufev),
 +                                 TO_DIR_CONN(conn)->zlib_state,
 +                                 string, len, done);
 +    } else {
 +      r = bufferevent_write(conn->bufev, string, len);
 +    }
 +    if (r < 0) {
 +      /* XXXX mark for close? */
 +      log_warn(LD_NET, "bufferevent_write failed! That shouldn't happen.");
 +    }
 +    return;
 +  });
 +
    old_datalen = buf_datalen(conn->outbuf);
    if (zlib) {
      dir_connection_t *dir_conn = TO_DIR_CONN(conn);
@@@ -3402,32 -3028,6 +3405,32 @@@ connection_get_by_type_state_rendquery(
    return NULL;
  }
  
 +/** Return a directory connection (if any one exists) that is fetching
 + * the item described by <b>state</b>/<b>resource</b> */
 +dir_connection_t *
 +connection_dir_get_by_purpose_and_resource(int purpose,
 +                                           const char *resource)
 +{
 +  smartlist_t *conns = get_connection_array();
 +
 +  SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
 +    dir_connection_t *dirconn;
 +    if (conn->type != CONN_TYPE_DIR || conn->marked_for_close ||
 +        conn->purpose != purpose)
 +      continue;
 +    dirconn = TO_DIR_CONN(conn);
 +    if (dirconn->requested_resource == NULL) {
 +      if (resource == NULL)
 +        return dirconn;
 +    } else if (resource) {
 +      if (0 == strcmp(resource, dirconn->requested_resource))
 +        return dirconn;
 +    }
 +  } SMARTLIST_FOREACH_END(conn);
 +
 +  return NULL;
 +}
 +
  /** Return an open, non-marked connection of a given type and purpose, or NULL
   * if no such connection exists. */
  connection_t *
@@@ -3667,9 -3267,6 +3670,9 @@@ connection_finished_flushing(connection
  
  //  log_fn(LOG_DEBUG,"entered. Socket %u.", conn->s);
  
 +  IF_HAS_NO_BUFFEREVENT(conn)
 +    connection_stop_writing(conn);
 +
    switch (conn->type) {
      case CONN_TYPE_OR:
        return connection_or_finished_flushing(TO_OR_CONN(conn));
@@@ -3795,16 -3392,6 +3798,16 @@@ assert_connection_ok(connection_t *conn
    tor_assert(conn);
    tor_assert(conn->type >= _CONN_TYPE_MIN);
    tor_assert(conn->type <= _CONN_TYPE_MAX);
 +
 +#ifdef USE_BUFFEREVENTS
 +  if (conn->bufev) {
 +    tor_assert(conn->read_event == NULL);
 +    tor_assert(conn->write_event == NULL);
 +    tor_assert(conn->inbuf == NULL);
 +    tor_assert(conn->outbuf == NULL);
 +  }
 +#endif
 +
    switch (conn->type) {
      case CONN_TYPE_OR:
        tor_assert(conn->magic == OR_CONNECTION_MAGIC);
@@@ -3843,10 -3430,10 +3846,10 @@@
     * marked_for_close. */
  
    /* buffers */
 -  if (!connection_is_listener(conn)) {
 +  if (conn->inbuf)
      assert_buf_ok(conn->inbuf);
 +  if (conn->outbuf)
      assert_buf_ok(conn->outbuf);
 -  }
  
    if (conn->type == CONN_TYPE_OR) {
      or_connection_t *or_conn = TO_OR_CONN(conn);
diff --combined src/or/connection_edge.c
index c0b177d,f02479f..bb36f08
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@@ -23,7 -23,6 +23,7 @@@
  #include "dirserv.h"
  #include "hibernate.h"
  #include "main.h"
 +#include "nodelist.h"
  #include "policies.h"
  #include "reasons.h"
  #include "relay.h"
@@@ -91,8 -90,8 +91,8 @@@ _connection_mark_unattached_ap(edge_con
        conn->socks_request->has_finished = 1;
    }
  
 -  _connection_mark_for_close(TO_CONN(conn), line, file);
 -  conn->_base.hold_open_until_flushed = 1;
 +  _connection_mark_and_flush(TO_CONN(conn), line, file);
 +
    conn->end_reason = endreason;
  }
  
@@@ -101,7 -100,7 +101,7 @@@
  int
  connection_edge_reached_eof(edge_connection_t *conn)
  {
 -  if (buf_datalen(conn->_base.inbuf) &&
 +  if (connection_get_inbuf_len(TO_CONN(conn)) &&
        connection_state_is_open(TO_CONN(conn))) {
      /* it still has stuff to process. don't let it die yet. */
      return 0;
@@@ -192,7 -191,8 +192,7 @@@ connection_edge_destroy(circid_t circ_i
        conn->edge_has_sent_end = 1;
        conn->end_reason = END_STREAM_REASON_DESTROY;
        conn->end_reason |= END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED;
 -      connection_mark_for_close(TO_CONN(conn));
 -      conn->_base.hold_open_until_flushed = 1;
 +      connection_mark_and_flush(TO_CONN(conn));
      }
    }
    conn->cpath_layer = NULL;
@@@ -319,6 -319,7 +319,6 @@@ connection_edge_finished_flushing(edge_
    switch (conn->_base.state) {
      case AP_CONN_STATE_OPEN:
      case EXIT_CONN_STATE_OPEN:
 -      connection_stop_writing(TO_CONN(conn));
        connection_edge_consider_sending_sendme(conn);
        return 0;
      case AP_CONN_STATE_SOCKS_WAIT:
@@@ -327,7 -328,7 +327,7 @@@
      case AP_CONN_STATE_CIRCUIT_WAIT:
      case AP_CONN_STATE_CONNECT_WAIT:
      case AP_CONN_STATE_CONTROLLER_WAIT:
 -      connection_stop_writing(TO_CONN(conn));
 +    case AP_CONN_STATE_RESOLVE_WAIT:
        return 0;
      default:
        log_warn(LD_BUG, "Called in unexpected state %d.",conn->_base.state);
@@@ -357,9 -358,8 +357,9 @@@ connection_edge_finished_connecting(edg
    rep_hist_note_exit_stream_opened(conn->port);
  
    conn->state = EXIT_CONN_STATE_OPEN;
 -  connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */
 -  if (connection_wants_to_flush(conn)) /* in case there are any queued relay
 +  IF_HAS_NO_BUFFEREVENT(conn)
 +    connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */
 +  if (connection_get_outbuf_len(conn)) /* in case there are any queued relay
                                          * cells */
      connection_start_writing(conn);
    /* deliver a 'connected' relay cell back through the circuit. */
@@@ -587,7 -587,7 +587,7 @@@ voi
  circuit_discard_optional_exit_enclaves(extend_info_t *info)
  {
    edge_connection_t *edge_conn;
 -  routerinfo_t *r1, *r2;
 +  const node_t *r1, *r2;
  
    smartlist_t *conns = get_connection_array();
    SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
@@@ -599,8 -599,8 +599,8 @@@
      if (!edge_conn->chosen_exit_optional &&
          !edge_conn->chosen_exit_retries)
        continue;
 -    r1 = router_get_by_nickname(edge_conn->chosen_exit_name, 0);
 -    r2 = router_get_by_digest(info->identity_digest);
 +    r1 = node_get_by_nickname(edge_conn->chosen_exit_name, 0);
 +    r2 = node_get_by_id(info->identity_digest);
      if (!r1 || !r2 || r1 != r2)
        continue;
      tor_assert(edge_conn->socks_request);
@@@ -1199,6 -1199,7 +1199,6 @@@ static char 
  addressmap_get_virtual_address(int type)
  {
    char buf[64];
 -  struct in_addr in;
    tor_assert(addressmap);
  
    if (type == RESOLVED_TYPE_HOSTNAME) {
@@@ -1212,7 -1213,6 +1212,7 @@@
    } else if (type == RESOLVED_TYPE_IPV4) {
      // This is an imperfect estimate of how many addresses are available, but
      // that's ok.
 +    struct in_addr in;
      uint32_t available = 1u << (32-virtual_addr_netmask_bits);
      while (available) {
        /* Don't hand out any .0 or .255 address. */
@@@ -1605,12 -1605,12 +1605,12 @@@ connection_ap_handshake_rewrite_and_att
          return -1;
        }
      } else {
 -      routerinfo_t *r;
 +      const node_t *r;
        conn->chosen_exit_name = tor_strdup(socks->address);
 -      r = router_get_by_nickname(conn->chosen_exit_name, 1);
 +      r = node_get_by_nickname(conn->chosen_exit_name, 1);
        *socks->address = 0;
        if (r) {
 -        strlcpy(socks->address, r->address, sizeof(socks->address));
 +        node_get_address_string(r, socks->address, sizeof(socks->address));
        } else {
          log_warn(LD_APP,
                   "Unrecognized server in exit address '%s.exit'. Refusing.",
@@@ -1659,19 -1659,41 +1659,41 @@@
          connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
          return -1;
        }
+       if (options->ClientRejectInternalAddresses &&
+           !conn->use_begindir && !conn->chosen_exit_name && !circ) {
+         tor_addr_t addr;
+         if (tor_addr_from_str(&addr, socks->address) >= 0 &&
+             tor_addr_is_internal(&addr, 0)) {
+           /* If this is an explicit private address with no chosen exit node,
+            * then we really don't want to try to connect to it.  That's
+            * probably an error. */
+           if (conn->is_transparent_ap) {
+             log_warn(LD_NET,
+                      "Rejecting request for anonymous connection to private "
+                      "address %s on a TransPort or NATDPort.  Possible loop "
+                      "in your NAT rules?", safe_str_client(socks->address));
+           } else {
+             log_warn(LD_NET,
+                      "Rejecting SOCKS request for anonymous connection to "
+                      "private address %s", safe_str_client(socks->address));
+           }
+           connection_mark_unattached_ap(conn, END_STREAM_REASON_PRIVATE_ADDR);
+           return -1;
+         }
+       }
  
        if (!conn->use_begindir && !conn->chosen_exit_name && !circ) {
          /* see if we can find a suitable enclave exit */
 -        routerinfo_t *r =
 +        const node_t *r =
            router_find_exact_exit_enclave(socks->address, socks->port);
          if (r) {
            log_info(LD_APP,
                     "Redirecting address %s to exit at enclave router %s",
 -                   safe_str_client(socks->address), r->nickname);
 +                   safe_str_client(socks->address), node_get_nickname(r));
            /* use the hex digest, not nickname, in case there are two
               routers with this nickname */
            conn->chosen_exit_name =
 -            tor_strdup(hex_str(r->cache_info.identity_digest, DIGEST_LEN));
 +            tor_strdup(hex_str(r->identity, DIGEST_LEN));
            conn->chosen_exit_optional = 1;
          }
        }
@@@ -1786,10 -1808,10 +1808,10 @@@ get_pf_socket(void
  
  #ifdef OPENBSD
    /* only works on OpenBSD */
 -  pf = open("/dev/pf", O_RDONLY);
 +  pf = tor_open_cloexec("/dev/pf", O_RDONLY, 0);
  #else
    /* works on NetBSD and FreeBSD */
 -  pf = open("/dev/pf", O_RDWR);
 +  pf = tor_open_cloexec("/dev/pf", O_RDWR, 0);
  #endif
  
    if (pf < 0) {
@@@ -1926,14 -1948,8 +1948,14 @@@ connection_ap_handshake_process_socks(e
  
    log_debug(LD_APP,"entered.");
  
 -  sockshere = fetch_from_buf_socks(conn->_base.inbuf, socks,
 -                                   options->TestSocks, options->SafeSocks);
 +  IF_HAS_BUFFEREVENT(TO_CONN(conn), {
 +    struct evbuffer *input = bufferevent_get_input(conn->_base.bufev);
 +    sockshere = fetch_from_evbuffer_socks(input, socks,
 +                                     options->TestSocks, options->SafeSocks);
 +  }) ELSE_IF_NO_BUFFEREVENT {
 +    sockshere = fetch_from_buf_socks(conn->_base.inbuf, socks,
 +                                     options->TestSocks, options->SafeSocks);
 +  };
    if (sockshere == 0) {
      if (socks->replylen) {
        connection_write_to_buf(socks->reply, socks->replylen, TO_CONN(conn));
@@@ -2034,7 -2050,7 +2056,7 @@@ connection_ap_process_natd(edge_connect
  
    /* look for LF-terminated "[DEST ip_addr port]"
     * where ip_addr is a dotted-quad and port is in string form */
 -  err = fetch_from_buf_line(conn->_base.inbuf, tmp_buf, &tlen);
 +  err = connection_fetch_from_buf_line(TO_CONN(conn), tmp_buf, &tlen);
    if (err == 0)
      return 0;
    if (err < 0) {
@@@ -2141,10 -2157,8 +2163,10 @@@ connection_ap_handshake_send_begin(edge
                 ap_conn->socks_request->port);
    payload_len = (int)strlen(payload)+1;
  
 -  log_debug(LD_APP,
 -            "Sending relay cell to begin stream %d.", ap_conn->stream_id);
 +  log_info(LD_APP,
 +           "Sending relay cell %d to begin stream %d.",
 +           (int)ap_conn->use_begindir,
 +           ap_conn->stream_id);
  
    begin_type = ap_conn->use_begindir ?
                   RELAY_COMMAND_BEGIN_DIR : RELAY_COMMAND_BEGIN;
@@@ -2252,11 -2266,9 +2274,11 @@@ connection_ap_handshake_send_resolve(ed
   * and call connection_ap_handshake_attach_circuit(conn) on it.
   *
   * Return the other end of the linked connection pair, or -1 if error.
 + * DOCDOC partner.
   */
  edge_connection_t *
 -connection_ap_make_link(char *address, uint16_t port,
 +connection_ap_make_link(connection_t *partner,
 +                        char *address, uint16_t port,
                          const char *digest, int use_begindir, int want_onehop)
  {
    edge_connection_t *conn;
@@@ -2291,8 -2303,6 +2313,8 @@@
    tor_addr_make_unspec(&conn->_base.addr);
    conn->_base.port = 0;
  
 +  connection_link_connections(partner, TO_CONN(conn));
 +
    if (connection_add(TO_CONN(conn)) < 0) { /* no space, forget it */
      connection_free(TO_CONN(conn));
      return NULL;
@@@ -2329,11 -2339,13 +2351,11 @@@ tell_controller_about_resolved_result(e
                     answer_type == RESOLVED_TYPE_HOSTNAME)) {
      return; /* we already told the controller. */
    } else if (answer_type == RESOLVED_TYPE_IPV4 && answer_len >= 4) {
 -    struct in_addr in;
 -    char buf[INET_NTOA_BUF_LEN];
 -    in.s_addr = get_uint32(answer);
 -    tor_inet_ntoa(&in, buf, sizeof(buf));
 +    char *cp = tor_dup_ip(get_uint32(answer));
      control_event_address_mapped(conn->socks_request->address,
 -                                 buf, expires, NULL);
 -  } else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len <256) {
 +                                 cp, expires, NULL);
 +    tor_free(cp);
 +  } else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len < 256) {
      char *cp = tor_strndup(answer, answer_len);
      control_event_address_mapped(conn->socks_request->address,
                                   cp, expires, NULL);
@@@ -2812,13 -2824,12 +2834,13 @@@ connection_exit_connect(edge_connection
    }
  
    conn->state = EXIT_CONN_STATE_OPEN;
 -  if (connection_wants_to_flush(conn)) {
 +  if (connection_get_outbuf_len(conn)) {
      /* in case there are any queued data cells */
      log_warn(LD_BUG,"newly connected conn had data waiting!");
  //    connection_start_writing(conn);
    }
 -  connection_watch_events(conn, READ_EVENT);
 +  IF_HAS_NO_BUFFEREVENT(conn)
 +    connection_watch_events(conn, READ_EVENT);
  
    /* also, deliver a 'connected' cell back through the circuit. */
    if (connection_edge_is_rendezvous_stream(edge_conn)) {
@@@ -2931,7 -2942,7 +2953,7 @@@ connection_edge_is_rendezvous_stream(ed
   * this relay, return 0.
   */
  int
 -connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit,
 +connection_ap_can_use_exit(edge_connection_t *conn, const node_t *exit,
                             int excluded_means_no)
  {
    or_options_t *options = get_options();
@@@ -2945,10 -2956,10 +2967,10 @@@
     * make sure the exit node of the existing circuit matches exactly.
     */
    if (conn->chosen_exit_name) {
 -    routerinfo_t *chosen_exit =
 -      router_get_by_nickname(conn->chosen_exit_name, 1);
 -    if (!chosen_exit || memcmp(chosen_exit->cache_info.identity_digest,
 -                               exit->cache_info.identity_digest, DIGEST_LEN)) {
 +    const node_t *chosen_exit =
 +      node_get_by_nickname(conn->chosen_exit_name, 1);
 +    if (!chosen_exit || memcmp(chosen_exit->identity,
 +                               exit->identity, DIGEST_LEN)) {
        /* doesn't match */
  //      log_debug(LD_APP,"Requested node '%s', considering node '%s'. No.",
  //                conn->chosen_exit_name, exit->nickname);
@@@ -2963,7 -2974,8 +2985,7 @@@
      addr_policy_result_t r;
      if (tor_inet_aton(conn->socks_request->address, &in))
        addr = ntohl(in.s_addr);
 -    r = compare_addr_to_addr_policy(addr, conn->socks_request->port,
 -                                    exit->exit_policy);
 +    r = compare_addr_to_node_policy(addr, conn->socks_request->port, exit);
      if (r == ADDR_POLICY_REJECTED)
        return 0; /* We know the address, and the exit policy rejects it. */
      if (r == ADDR_POLICY_PROBABLY_REJECTED && !conn->chosen_exit_name)
@@@ -2971,13 -2983,18 +2993,13 @@@
                   * addresses with this port. Since the user didn't ask for
                   * this node, err on the side of caution. */
    } else if (SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)) {
 -    /* Can't support reverse lookups without eventdns. */
 -    if (conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR &&
 -        exit->has_old_dnsworkers)
 -      return 0;
 -
      /* Don't send DNS requests to non-exit servers by default. */
 -    if (!conn->chosen_exit_name && policy_is_reject_star(exit->exit_policy))
 +    if (!conn->chosen_exit_name && node_exit_policy_rejects_all(exit))
        return 0;
    }
    if (options->_ExcludeExitNodesUnion &&
        (options->StrictNodes || excluded_means_no) &&
 -      routerset_contains_router(options->_ExcludeExitNodesUnion, exit)) {
 +      routerset_contains_node(options->_ExcludeExitNodesUnion, exit)) {
      /* If we are trying to avoid this node as exit, and we have StrictNodes
       * set, then this is not a suitable exit. Refuse it.
       *
diff --combined src/or/or.h
index 9672b41,752de21..9bf46e9
--- a/src/or/or.h
+++ b/src/or/or.h
@@@ -83,13 -83,6 +83,13 @@@
  #define snprintf _snprintf
  #endif
  
 +#ifdef USE_BUFFEREVENTS
 +#include <event2/bufferevent.h>
 +#include <event2/buffer.h>
 +#include <event2/util.h>
 +#endif
 +
 +#include "crypto.h"
  #include "tortls.h"
  #include "../common/torlog.h"
  #include "container.h"
@@@ -391,9 -384,7 +391,9 @@@ typedef enum 
  /** A connection to a hidden service directory server: download a v2 rendezvous
   * descriptor. */
  #define DIR_PURPOSE_FETCH_RENDDESC_V2 18
 -#define _DIR_PURPOSE_MAX 18
 +/** A connection to a directory server: download a microdescriptor. */
 +#define DIR_PURPOSE_FETCH_MICRODESC 19
 +#define _DIR_PURPOSE_MAX 19
  
  /** True iff <b>p</b> is a purpose corresponding to uploading data to a
   * directory server. */
@@@ -592,6 -583,9 +592,9 @@@
  /** This is a connection on the NATD port, and the destination IP:Port was
   * either ill-formed or out-of-range. */
  #define END_STREAM_REASON_INVALID_NATD_DEST 261
+ /** The target address is in a private network (like 127.0.0.1 or 10.0.0.1);
+  * you don't want to do that over a randomly chosen exit */
+ #define END_STREAM_REASON_PRIVATE_ADDR 262
  
  /** Bitwise-and this value with endreason to mask out all flags. */
  #define END_STREAM_REASON_MASK 511
@@@ -811,9 -805,6 +814,9 @@@ typedef enum 
   * Tor 0.1.2.x is obsolete, we can remove this. */
  #define DEFAULT_CLIENT_NICKNAME "client"
  
 +/** Name chosen by routers that don't configure nicknames */
 +#define UNNAMED_ROUTER_NICKNAME "Unnamed"
 +
  /** Number of bytes in a SOCKS4 header. */
  #define SOCKS4_NETWORK_LEN 8
  
@@@ -859,8 -850,8 +862,8 @@@ typedef struct cell_t 
  typedef struct var_cell_t {
    uint8_t command;
    circid_t circ_id;
 -  uint16_t payload_len;
 -  uint8_t payload[1];
 +  uint16_t payload_len; /**< The actual length of <b>payload</b>. */
 +  uint8_t payload[FLEXIBLE_ARRAY_MEMBER];
  } var_cell_t;
  
  /** A cell as packed for writing to the network. */
@@@ -977,7 -968,6 +980,7 @@@ typedef struct connection_t 
    /** Our socket; -1 if this connection is closed, or has no socket. */
    evutil_socket_t s;
    int conn_array_index; /**< Index into the global connection array. */
 +
    struct event *read_event; /**< Libevent event structure. */
    struct event *write_event; /**< Libevent event structure. */
    buf_t *inbuf; /**< Buffer holding data read over this connection. */
@@@ -988,11 -978,6 +991,11 @@@
                                * read? */
    time_t timestamp_lastwritten; /**< When was the last time libevent said we
                                   * could write? */
 +
 +#ifdef USE_BUFFEREVENTS
 +  struct bufferevent *bufev; /**< A Libevent buffered IO structure. */
 +#endif
 +
    time_t timestamp_created; /**< When was this connection_t created? */
  
    /* XXXX_IP6 make this IPv6-capable */
@@@ -1092,16 -1077,10 +1095,16 @@@ typedef struct or_connection_t 
    /* bandwidth* and *_bucket only used by ORs in OPEN state: */
    int bandwidthrate; /**< Bytes/s added to the bucket. (OPEN ORs only.) */
    int bandwidthburst; /**< Max bucket size for this conn. (OPEN ORs only.) */
 +#ifndef USE_BUFFEREVENTS
    int read_bucket; /**< When this hits 0, stop receiving. Every second we
                      * add 'bandwidthrate' to this, capping it at
                      * bandwidthburst. (OPEN ORs only) */
    int write_bucket; /**< When this hits 0, stop writing. Like read_bucket. */
 +#else
 +  /** DOCDOC */
 +  /* XXXX we could share this among all connections. */
 +  struct ev_token_bucket_cfg *bucket_cfg;
 +#endif
    int n_circuits; /**< How many circuits use this connection as p_conn or
                     * n_conn ? */
  
@@@ -1194,6 -1173,10 +1197,10 @@@ typedef struct edge_connection_t 
     * zero, abandon the associated mapaddress. */
    unsigned int chosen_exit_retries:3;
  
+   /** True iff this is an AP connection that came from a transparent or
+    * NATd connection */
+   unsigned int is_transparent_ap:1;
+ 
    /** If this is a DNSPort connection, this field holds the pending DNS
     * request that we're going to try to answer.  */
    struct evdns_server_request *dns_server_request;
@@@ -1205,13 -1188,8 +1212,13 @@@
  typedef struct dir_connection_t {
    connection_t _base;
  
 -  char *requested_resource; /**< Which 'resource' did we ask the directory
 -                             * for? */
 + /** Which 'resource' did we ask the directory for? This is typically the part
 +  * of the URL string that defines, relative to the directory conn purpose,
 +  * what thing we want.  For example, in router descriptor downloads by
 +  * descriptor digest, it contains "d/", then one ore more +-separated
 +  * fingerprints.
 +  **/
 +  char *requested_resource;
    unsigned int dirconn_direct:1; /**< Is this dirconn direct, or via Tor? */
  
    /* Used only for server sides of some dir connections, to implement
@@@ -1302,51 -1280,6 +1309,51 @@@ static INLINE control_connection_t *TO_
    return DOWNCAST(control_connection_t, c);
  }
  
 +/* Conditional macros to help write code that works whether bufferevents are
 +   disabled or not.
 +
 +   We can't just write:
 +      if (conn->bufev) {
 +        do bufferevent stuff;
 +      } else {
 +        do other stuff;
 +      }
 +   because the bufferevent stuff won't even compile unless we have a fairly
 +   new version of Libevent.  Instead, we say:
 +      IF_HAS_BUFFEREVENT(conn, { do_bufferevent_stuff } );
 +   or:
 +      IF_HAS_BUFFEREVENT(conn, {
 +        do bufferevent stuff;
 +      }) ELSE_IF_NO_BUFFEREVENT {
 +        do non-bufferevent stuff;
 +      }
 +   If we're compiling with bufferevent support, then the macros expand more or
 +   less to:
 +      if (conn->bufev) {
 +        do_bufferevent_stuff;
 +      } else {
 +        do non-bufferevent stuff;
 +      }
 +   and if we aren't using bufferevents, they expand more or less to:
 +      { do non-bufferevent stuff; }
 +*/
 +#ifdef USE_BUFFEREVENTS
 +#define HAS_BUFFEREVENT(c) (((c)->bufev) != NULL)
 +#define IF_HAS_BUFFEREVENT(c, stmt)                \
 +  if ((c)->bufev) do {                             \
 +      stmt ;                                       \
 +  } while (0)
 +#define ELSE_IF_NO_BUFFEREVENT ; else
 +#define IF_HAS_NO_BUFFEREVENT(c)                   \
 +  if (NULL == (c)->bufev)
 +#else
 +#define HAS_BUFFEREVENT(c) (0)
 +#define IF_HAS_BUFFEREVENT(c, stmt) (void)0
 +#define ELSE_IF_NO_BUFFEREVENT ;
 +#define IF_HAS_NO_BUFFEREVENT(c)                \
 +  if (1)
 +#endif
 +
  /** What action type does an address policy indicate: accept or reject? */
  typedef enum {
    ADDR_POLICY_ACCEPT=1,
@@@ -1510,49 -1443,59 +1517,49 @@@ typedef struct 
    char *contact_info; /**< Declared contact info for this router. */
    unsigned int is_hibernating:1; /**< Whether the router claims to be
                                    * hibernating */
 -  unsigned int has_old_dnsworkers:1; /**< Whether the router is using
 -                                      * dnsworker code. */
 -  unsigned int caches_extra_info:1; /**< Whether the router caches and serves
 -                                     * extrainfo documents. */
 -  unsigned int allow_single_hop_exits:1;  /**< Whether the router allows
 -                                     * single hop exits. */
 -
 -  /* local info */
 -  unsigned int is_running:1; /**< As far as we know, is this OR currently
 -                              * running? */
 -  unsigned int is_valid:1; /**< Has a trusted dirserver validated this OR?
 -                               *  (For Authdir: Have we validated this OR?)
 -                               */
 -  unsigned int is_named:1; /**< Do we believe the nickname that this OR gives
 -                            * us? */
 -  unsigned int is_fast:1; /** Do we think this is a fast OR? */
 -  unsigned int is_stable:1; /** Do we think this is a stable OR? */
 -  unsigned int is_possible_guard:1; /**< Do we think this is an OK guard? */
 -  unsigned int is_exit:1; /**< Do we think this is an OK exit? */
 -  unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked,
 -                               * or otherwise nasty? */
 -  unsigned int is_bad_directory:1; /**< Do we think this directory is junky,
 -                                    * underpowered, or otherwise useless? */
 +  unsigned int caches_extra_info:1; /**< Whether the router says it caches and
 +                                     * serves extrainfo documents. */
 +  unsigned int allow_single_hop_exits:1;  /**< Whether the router says
 +                                           * it allows single hop exits. */
 +
    unsigned int wants_to_be_hs_dir:1; /**< True iff this router claims to be
                                        * a hidden service directory. */
 -  unsigned int is_hs_dir:1; /**< True iff this router is a hidden service
 -                             * directory according to the authorities. */
    unsigned int policy_is_reject_star:1; /**< True iff the exit policy for this
                                           * router rejects everything. */
    /** True if, after we have added this router, we should re-launch
     * tests for it. */
    unsigned int needs_retest_if_added:1;
  
 -/** Tor can use this router for general positions in circuits. */
 +/** Tor can use this router for general positions in circuits; we got it
 + * from a directory server as usual, or we're an authority and a server
 + * uploaded it. */
  #define ROUTER_PURPOSE_GENERAL 0
 -/** Tor should avoid using this router for circuit-building. */
 +/** Tor should avoid using this router for circuit-building: we got it
 + * from a crontroller.  If the controller wants to use it, it'll have to
 + * ask for it by identity. */
  #define ROUTER_PURPOSE_CONTROLLER 1
 -/** Tor should use this router only for bridge positions in circuits. */
 +/** Tor should use this router only for bridge positions in circuits: we got
 + * it via a directory request from the bridge itself, or a bridge
 + * authority. x*/
  #define ROUTER_PURPOSE_BRIDGE 2
  /** Tor should not use this router; it was marked in cached-descriptors with
   * a purpose we didn't recognize. */
  #define ROUTER_PURPOSE_UNKNOWN 255
  
 -  uint8_t purpose; /** What positions in a circuit is this router good for? */
 +  /* In what way did we find out about this router?  One of ROUTER_PURPOSE_*.
 +   * Routers of different purposes are kept segregated and used for different
 +   * things; see notes on ROUTER_PURPOSE_* macros above.
 +   */
 +  uint8_t purpose;
  
    /* The below items are used only by authdirservers for
     * reachability testing. */
 +
    /** When was the last time we could reach this OR? */
    time_t last_reachable;
    /** When did we start testing reachability for this OR? */
    time_t testing_since;
 -  /** According to the geoip db what country is this router in? */
 -  country_t country;
 +
  } routerinfo_t;
  
  /** Information needed to keep and cache a signed extra-info document. */
@@@ -1578,9 -1521,8 +1585,9 @@@ typedef struct routerstatus_t 
                                        * has. */
    char identity_digest[DIGEST_LEN]; /**< Digest of the router's identity
                                       * key. */
 -  char descriptor_digest[DIGEST_LEN]; /**< Digest of the router's most recent
 -                                       * descriptor. */
 +  /** Digest of the router's most recent descriptor or microdescriptor.
 +   * If it's a descriptor, we only use the first DIGEST_LEN bytes. */
 +  char descriptor_digest[DIGEST256_LEN];
    uint32_t addr; /**< IPv4 address for this router. */
    uint16_t or_port; /**< OR port for this router. */
    uint16_t dir_port; /**< Directory port for this router. */
@@@ -1588,11 -1530,7 +1595,11 @@@
    unsigned int is_exit:1; /**< True iff this router is a good exit. */
    unsigned int is_stable:1; /**< True iff this router stays up a long time. */
    unsigned int is_fast:1; /**< True iff this router has good bandwidth. */
 -  unsigned int is_running:1; /**< True iff this router is up. */
 +  /** True iff this router is called 'running' in the consensus. We give it
 +   * this funny name so that we don't accidentally use this bit as a view of
 +   * whether we think the router is *currently* running.  If that's what you
 +   * want to know, look at is_running in node_t. */
 +  unsigned int is_flagged_running:1;
    unsigned int is_named:1; /**< True iff "nickname" belongs to this router. */
    unsigned int is_unnamed:1; /**< True iff "nickname" belongs to another
                                * router. */
@@@ -1644,31 -1582,15 +1651,31 @@@
     * from this authority.)  Applies in v2 networkstatus document only.
     */
    unsigned int need_to_mirror:1;
 -  unsigned int name_lookup_warned:1; /**< Have we warned the user for referring
 -                                      * to this (unnamed) router by nickname?
 -                                      */
    time_t last_dir_503_at; /**< When did this router last tell us that it
                             * was too busy to serve directory info? */
    download_status_t dl_status;
  
  } routerstatus_t;
  
 +/** A single entry in a parsed policy summary, describing a range of ports. */
 +typedef struct short_policy_entry_t {
 +  uint16_t min_port, max_port;
 +} short_policy_entry_t;
 +
 +/** A short_poliy_t is the parsed version of a policy summary. */
 +typedef struct short_policy_t {
 +  /** True if the members of 'entries' are port ranges to accept; false if
 +   * they are port ranges to reject */
 +  unsigned int is_accept : 1;
 +  /** The actual number of values in 'entries'. */
 +  unsigned int n_entries : 31;
 +  /** An array of 0 or more short_policy_entry_t values, each describing a
 +   * range of ports that this policy accepts or rejects (depending on the
 +   * value of is_accept).
 +   */
 +  short_policy_entry_t entries[FLEXIBLE_ARRAY_MEMBER];
 +} short_policy_t;
 +
  /** A microdescriptor is the smallest amount of information needed to build a
   * circuit through a router.  They are generated by the directory authorities,
   * using information from the uploaded routerinfo documents.  They are not
@@@ -1710,83 -1632,15 +1717,83 @@@ typedef struct microdesc_t 
    crypto_pk_env_t *onion_pkey;
    /** As routerinfo_t.family */
    smartlist_t *family;
 -  /** Encoded exit policy summary */
 -  char *exitsummary; /**< exit policy summary -
 -                      * XXX this probably should not stay a string. */
 +  /** Exit policy summary */
 +  short_policy_t *exit_policy;
  } microdesc_t;
  
 +/** A node_t represents a Tor router.
 + *
 + * Specifically, a node_t is a Tor router as we are using it: a router that
 + * we are considering for circuits, connections, and so on.  A node_t is a
 + * thin wrapper around the routerstatus, routerinfo, and microdesc for a
 + * single wrapper, and provides a consistent interface for all of them.
 + *
 + * Also, a node_t has mutable state.  While a routerinfo, a routerstatus,
 + * and a microdesc have[*] only the information read from a router
 + * descriptor, a consensus entry, and a microdescriptor (respectively)...
 + * a node_t has flags based on *our own current opinion* of the node.
 + *
 + * [*] Actually, there is some leftover information in each that is mutable.
 + *  We should try to excise that.
 + */
 +typedef struct node_t {
 +  /* Indexing information */
 +
 +  /** Used to look up the node_t by its identity digest. */
 +  HT_ENTRY(node_t) ht_ent;
 +  /** Position of the node within the list of nodes */
 +  int nodelist_idx;
 +
 +  /** The identity digest of this node_t.  No more than one node_t per
 +   * identity may exist at a time. */
 +  char identity[DIGEST_LEN];
 +
 +  microdesc_t *md;
 +  routerinfo_t *ri;
 +  routerstatus_t *rs;
 +
 +  /* local info: copied from routerstatus, then possibly frobbed based
 +   * on experience.  Authorities set this stuff directly. */
 +
 +  unsigned int is_running:1; /**< As far as we know, is this OR currently
 +                              * running? */
 +  unsigned int is_valid:1; /**< Has a trusted dirserver validated this OR?
 +                               *  (For Authdir: Have we validated this OR?)
 +                               */
 +  unsigned int is_fast:1; /** Do we think this is a fast OR? */
 +  unsigned int is_stable:1; /** Do we think this is a stable OR? */
 +  unsigned int is_possible_guard:1; /**< Do we think this is an OK guard? */
 +  unsigned int is_exit:1; /**< Do we think this is an OK exit? */
 +  unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked,
 +                               * or otherwise nasty? */
 +  unsigned int is_bad_directory:1; /**< Do we think this directory is junky,
 +                                    * underpowered, or otherwise useless? */
 +  unsigned int is_hs_dir:1; /**< True iff this router is a hidden service
 +                             * directory according to the authorities. */
 +
 +  /* Local info: warning state. */
 +
 +  unsigned int name_lookup_warned:1; /**< Have we warned the user for referring
 +                                      * to this (unnamed) router by nickname?
 +                                      */
 +
 +  /** Local info: we treat this node as if it rejects everything */
 +  unsigned int rejects_all:1;
 +
 +  /* Local info: derived. */
 +
 +  /** According to the geoip db what country is this router in? */
 +  country_t country;
 +} node_t;
 +
  /** How many times will we try to download a router's descriptor before giving
   * up? */
  #define MAX_ROUTERDESC_DOWNLOAD_FAILURES 8
  
 +/** How many times will we try to download a microdescriptor before giving
 + * up? */
 +#define MAX_MICRODESC_DOWNLOAD_FAILURES 8
 +
  /** Contents of a v2 (non-consensus, non-vote) network status object. */
  typedef struct networkstatus_v2_t {
    /** When did we receive the network-status document? */
@@@ -2262,12 -2116,10 +2269,12 @@@ typedef struct circuit_t 
      * length ONIONSKIN_CHALLENGE_LEN. */
    char *n_conn_onionskin;
  
 -  time_t timestamp_created; /**< When was this circuit created? */
 +  /** When was this circuit created?  We keep this timestamp with a higher
 +   * resolution than most so that the circuit-build-time tracking code can
 +   * get millisecond resolution. */
 +  struct timeval timestamp_created;
    time_t timestamp_dirty; /**< When the circuit was first used, or 0 if the
                             * circuit is clean. */
 -  struct timeval highres_created; /**< When exactly was the circuit created? */
  
    uint16_t marked_for_close; /**< Should we close this circuit at the end of
                                * the main loop? (If true, holds the line number
@@@ -2492,7 -2344,6 +2499,7 @@@ typedef struct 
  
    config_line_t *Logs; /**< New-style list of configuration lines
                          * for logs */
 +  int LogTimeGranularity; /**< Log resolution in milliseconds. */
  
    char *DebugLogFile; /**< Where to send verbose log messages. */
    char *DataDirectory; /**< OR only: where to store long-term data. */
@@@ -2749,8 -2600,7 +2756,8 @@@
  
    char *MyFamily; /**< Declared family for this OR. */
    config_line_t *NodeFamilies; /**< List of config lines for
 -                                       * node families */
 +                                * node families */
 +  smartlist_t *NodeFamilySets; /**< List of parsed NodeFamilies values. */
    config_line_t *AuthDirBadDir; /**< Address policy for descriptors to
                                   * mark as bad dir mirrors. */
    config_line_t *AuthDirBadExit; /**< Address policy for descriptors to
@@@ -2855,10 -2705,6 +2862,10 @@@
                         * possible. */
    int PreferTunneledDirConns; /**< If true, avoid dirservers that don't
                                 * support BEGIN_DIR, when possible. */
 +  int PortForwarding; /**< If true, use NAT-PMP or UPnP to automatically
 +                       * forward the DirPort and ORPort on the NAT device */
 +  char *PortForwardingHelper; /** < Filename or full path of the port
 +                                  forwarding helper executable */
    int AllowNonRFC953Hostnames; /**< If true, we allow connections to hostnames
                                  * with weird characters. */
    /** If true, we try resolving hostnames with weird characters. */
@@@ -2896,9 -2742,6 +2903,9 @@@
    /** If true, the user wants us to collect statistics on port usage. */
    int ExitPortStatistics;
  
 +  /** If true, the user wants us to collect connection statistics. */
 +  int ConnDirectionStatistics;
 +
    /** If true, the user wants us to collect cell statistics. */
    int CellStatistics;
  
@@@ -2913,6 -2756,10 +2920,10 @@@
     * Helps avoid some cross-site attacks. */
    int ClientDNSRejectInternalAddresses;
  
+   /** If true, do not accept any requests to connect to internal addresses
+    * over randomly chosen exits. */
+   int ClientRejectInternalAddresses;
+ 
    /** The length of time that we think a consensus should be fresh. */
    int V3AuthVotingInterval;
    /** The length of time we think it will take to distribute votes. */
@@@ -2991,12 -2838,6 +3002,12 @@@
     */
    double CircuitPriorityHalflife;
  
 +  /** If true, do not enable IOCP on windows with bufferevents, even if
 +   * we think we could. */
 +  int DisableIOCP;
 +  /** For testing only: will go away in 0.2.3.x. */
 +  int _UseFilteringSSLBufferevents;
 +
  } or_options_t;
  
  /** Persistent state for an onion router, as saved to disk. */
@@@ -3105,6 -2946,8 +3116,6 @@@ struct socks_request_t 
                                * every connection. */
  };
  
 -/* all the function prototypes go here */
 -
  /********************************* circuitbuild.c **********************/
  
  /** How many hops does a general-purpose circuit have by default? */
@@@ -3496,7 -3339,7 +3507,7 @@@ typedef enum 
    ADDR_POLICY_PROBABLY_ACCEPTED=1,
    /** Part of the address was unknown, but as far as we can tell, it was
     * rejected. */
 -  ADDR_POLICY_PROBABLY_REJECTED=2
 +  ADDR_POLICY_PROBABLY_REJECTED=2,
  } addr_policy_result_t;
  
  /********************************* rephist.c ***************************/
@@@ -3627,8 -3470,6 +3638,8 @@@ typedef struct trusted_dir_server_t 
   *  fetches to _any_ single directory server.]
   */
  #define PDS_NO_EXISTING_SERVERDESC_FETCH (1<<3)
 +#define PDS_NO_EXISTING_MICRODESC_FETCH (1<<4)
 +
  #define _PDS_PREFER_TUNNELED_DIR_CONNS (1<<16)
  
  /** Possible ways to weight routers when choosing one randomly.  See
@@@ -3646,8 -3487,7 +3657,8 @@@ typedef enum 
    CRN_NEED_GUARD = 1<<2,
    CRN_ALLOW_INVALID = 1<<3,
    /* XXXX not used, apparently. */
 -  CRN_WEIGHT_AS_EXIT = 1<<5
 +  CRN_WEIGHT_AS_EXIT = 1<<5,
 +  CRN_NEED_DESC = 1<<6
  } router_crn_flags_t;
  
  /** Return value for router_add_to_routerlist() and dirserv_add_descriptor() */