[or-cvs] r20669: {tor} Update SVN to match git master, revision 891b3d8633ba8f82 (in tor/trunk: . contrib contrib/auto-naming contrib/directory-archive debian debian/patches doc doc/spec doc/spec/proposals src src/common src/or src/test src/tools src/win32)

nickm at seul.org nickm at seul.org
Fri Sep 25 17:06:42 UTC 2009


Author: nickm
Date: 2009-09-25 13:06:41 -0400 (Fri, 25 Sep 2009)
New Revision: 20669

Added:
   tor/trunk/doc/spec/proposals/167-params-in-consensus.txt
   tor/trunk/doc/spec/proposals/168-reduce-circwindow.txt
   tor/trunk/src/common/sha256.c
   tor/trunk/src/common/tortls_states.h
   tor/trunk/src/test/
   tor/trunk/src/test/Makefile.am
   tor/trunk/src/test/test.c
   tor/trunk/src/test/test.h
   tor/trunk/src/test/test_addr.c
   tor/trunk/src/test/test_containers.c
   tor/trunk/src/test/test_crypto.c
   tor/trunk/src/test/test_data.c
   tor/trunk/src/test/test_dir.c
   tor/trunk/src/test/test_util.c
   tor/trunk/src/test/tinytest.c
   tor/trunk/src/test/tinytest.h
   tor/trunk/src/test/tinytest_demo.c
   tor/trunk/src/test/tinytest_macros.h
Modified:
   tor/trunk/ChangeLog
   tor/trunk/Makefile.am
   tor/trunk/configure.in
   tor/trunk/contrib/auto-naming/README
   tor/trunk/contrib/cross.sh
   tor/trunk/contrib/directory-archive/fetch-all
   tor/trunk/contrib/tor-mingw.nsi.in
   tor/trunk/contrib/torify.in
   tor/trunk/debian/changelog
   tor/trunk/debian/patches/03_tor_manpage_in_section_8.dpatch
   tor/trunk/debian/patches/06_add_compile_time_defaults.dpatch
   tor/trunk/debian/rules
   tor/trunk/doc/HACKING
   tor/trunk/doc/TODO.022
   tor/trunk/doc/spec/address-spec.txt
   tor/trunk/doc/spec/control-spec.txt
   tor/trunk/doc/spec/dir-spec.txt
   tor/trunk/doc/spec/proposals/000-index.txt
   tor/trunk/doc/spec/proposals/151-path-selection-improvements.txt
   tor/trunk/doc/spec/proposals/166-statistics-extra-info-docs.txt
   tor/trunk/doc/tor-osx-dmg-creation.txt
   tor/trunk/doc/tor.1.in
   tor/trunk/src/Makefile.am
   tor/trunk/src/common/Makefile.am
   tor/trunk/src/common/address.c
   tor/trunk/src/common/compat.c
   tor/trunk/src/common/container.c
   tor/trunk/src/common/container.h
   tor/trunk/src/common/crypto.c
   tor/trunk/src/common/crypto.h
   tor/trunk/src/common/log.c
   tor/trunk/src/common/log.h
   tor/trunk/src/common/torint.h
   tor/trunk/src/common/tortls.c
   tor/trunk/src/common/tortls.h
   tor/trunk/src/common/util.c
   tor/trunk/src/common/util.h
   tor/trunk/src/or/Makefile.am
   tor/trunk/src/or/buffers.c
   tor/trunk/src/or/circuitbuild.c
   tor/trunk/src/or/circuitlist.c
   tor/trunk/src/or/circuituse.c
   tor/trunk/src/or/command.c
   tor/trunk/src/or/config.c
   tor/trunk/src/or/connection.c
   tor/trunk/src/or/connection_edge.c
   tor/trunk/src/or/connection_or.c
   tor/trunk/src/or/control.c
   tor/trunk/src/or/directory.c
   tor/trunk/src/or/dirserv.c
   tor/trunk/src/or/dirvote.c
   tor/trunk/src/or/eventdns.c
   tor/trunk/src/or/eventdns.h
   tor/trunk/src/or/geoip.c
   tor/trunk/src/or/main.c
   tor/trunk/src/or/networkstatus.c
   tor/trunk/src/or/or.h
   tor/trunk/src/or/reasons.c
   tor/trunk/src/or/relay.c
   tor/trunk/src/or/rendclient.c
   tor/trunk/src/or/rendservice.c
   tor/trunk/src/or/rephist.c
   tor/trunk/src/or/router.c
   tor/trunk/src/or/routerlist.c
   tor/trunk/src/or/routerparse.c
   tor/trunk/src/or/tor_main.c
   tor/trunk/src/tools/tor-gencert.c
   tor/trunk/src/tools/tor-resolve.c
   tor/trunk/src/win32/orconfig.h
Log:
Update SVN to match git master, revision 891b3d8633ba8f82

Modified: tor/trunk/ChangeLog
===================================================================
--- tor/trunk/ChangeLog	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/ChangeLog	2009-09-25 17:06:41 UTC (rev 20669)
@@ -1,36 +1,158 @@
-Changes in version 0.2.2.1-alpha - 2009-0?-??
+Changes in version 0.2.2.4-alpha - 2009-??-??
+  o Minor features:
+    - Log SSL state transitions at debug level during handshake, and
+      include SSL states in error messages.  This may help debug
+      future SSL handshake issues.
+    - Add a new "Handshake" log domain for activities that happen
+      during the TLS handshake.
+    - Revert to the "June 3 2009" ip-to-country file. The September one
+      seems to have removed most US IP addresses.
+
+  o Code simplifications and refactoring:
+    - Revise our unit tests to use the "tinytest" framework, so we
+      can run tests in their own processes, have smarter setup/teardown
+      code, and so on.  The unit test code has moved to its own
+      subdirectory, and has been split into multiple modules.
+
+  o Minor bugfixes:
+    - Fix a couple of smaller issues with gathering statistics. Bugfixes
+      on 0.2.2.1-alpha.
+
+Changes in version 0.2.2.3-alpha - 2009-09-23
+  o Major bugfixes:
+    - Fix an overzealous assert in our new circuit build timeout code.
+      Bugfix on 0.2.2.2-alpha; fixes bug 1103.
+
+  o Minor bugfixes:
+    - If the networkstatus consensus tells us that we should use a
+      negative circuit package window, ignore it. Otherwise we'll
+      believe it and then trigger an assert. Bugfix on 0.2.2.2-alpha.
+
+
+Changes in version 0.2.2.2-alpha - 2009-09-21
   o Major features:
-    - Add support for dynamic OpenSSL hardware crypto acceleration engines
-      via new AccelName and AccelDir options.
+    - Tor now tracks how long it takes to build client-side circuits
+      over time, and adapts its timeout to local network performance.
+      Since a circuit that takes a long time to build will also provide
+      bad performance, we get significant latency improvements by
+      discarding the slowest 20% of circuits. Specifically, Tor creates
+      circuits more aggressively than usual until it has enough data
+      points for a good timeout estimate. Implements proposal 151.
+      We are especially looking for reports (good and bad) from users with
+      both EDGE and broadband connections that can move from broadband
+      to EDGE and find out if the build-time data in the .tor/state gets
+      reset without loss of Tor usability. You should also see a notice
+      log message telling you that Tor has reset its timeout.
+    - Directory authorities can now vote on arbitary integer values as
+      part of the consensus process. This is designed to help set
+      network-wide parameters. Implements proposal 167.
+    - Tor now reads the "circwindow" parameter out of the consensus,
+      and uses that value for its circuit package window rather than the
+      default of 1000 cells. Begins the implementation of proposal 168.
 
+  o Major bugfixes:
+    - Fix a remotely triggerable memory leak when a consensus document
+      contains more than one signature from the same voter. Bugfix on
+      0.2.0.3-alpha.
+
+  o Minor bugfixes:
+    - Fix an extremely rare infinite recursion bug that could occur if
+      we tried to log a message after shutting down the log subsystem.
+      Found by Matt Edman. Bugfix on 0.2.0.16-alpha.
+    - Fix parsing for memory or time units given without a space between
+      the number and the unit. Bugfix on 0.2.2.1-alpha; fixes bug 1076.
+    - A networkstatus vote must contain exactly one signature. Spec
+      conformance issue. Bugfix on 0.2.0.3-alpha.
+    - Fix an obscure bug where hidden services on 64-bit big-endian
+      systems might mis-read the timestamp in v3 introduce cells, and
+      refuse to connect back to the client. Discovered by "rotor".
+      Bugfix on 0.2.1.6-alpha.
+    - We were triggering a CLOCK_SKEW controller status event whenever
+      we connect via the v2 connection protocol to any relay that has
+      a wrong clock. Instead, we should only inform the controller when
+      it's a trusted authority that claims our clock is wrong. Bugfix
+      on 0.2.0.20-rc; starts to fix bug 1074. Reported by SwissTorExit.
+    - We were telling the controller about CHECKING_REACHABILITY and
+      REACHABILITY_FAILED status events whenever we launch a testing
+      circuit or notice that one has failed. Instead, only tell the
+      controller when we want to inform the user of overall success or
+      overall failure. Bugfix on 0.1.2.6-alpha. Fixes bug 1075. Reported
+      by SwissTorExit.
+    - Don't warn when we're using a circuit that ends with a node
+      excluded in ExcludeExitNodes, but the circuit is not used to access
+      the outside world. This should help fix bug 1090, but more problems
+      remain. Bugfix on 0.2.1.6-alpha.
+    - Work around a small memory leak in some versions of OpenSSL that
+      stopped the memory used by the hostname TLS extension from being
+      freed.
+    - Make our 'torify' script more portable; if we have only one of
+      'torsocks' or 'tsocks' installed, don't complain to the user;
+      and explain our warning about tsocks better.
+
+  o Minor features:
+    - Add a "getinfo status/accepted-server-descriptor" controller
+      command, which is the recommended way for controllers to learn
+      whether our server descriptor has been successfully received by at
+      least on directory authority. Un-recommend good-server-descriptor
+      getinfo and status events until we have a better design for them.
+    - Update to the "September 4 2009" ip-to-country file.
+
+
+Changes in version 0.2.2.1-alpha - 2009-08-26
+  o Security fixes:
+    - Start the process of disabling ".exit" address notation, since it
+      can be used for a variety of esoteric application-level attacks
+      on users. To reenable it, set "AllowDotExit 1" in your torrc. Fix
+      on 0.0.9rc5.
+
+  o New directory authorities:
+    - Set up urras (run by Jacob Appelbaum) as the seventh v3 directory
+      authority.
+
+  o Major features:
+    - New AccelName and AccelDir options add support for dynamic OpenSSL
+      hardware crypto acceleration engines.
+    - Tor now supports tunneling all of its outgoing connections over
+      a SOCKS proxy, using the SOCKS4Proxy and/or SOCKS5Proxy
+      configuration options. Code by Christopher Davis.
+
+  o Major bugfixes:
+    - Send circuit or stream sendme cells when our window has decreased
+      by 100 cells, not when it has decreased by 101 cells. Bug uncovered
+      by Karsten when testing the "reduce circuit window" performance
+      patch. Bugfix on the 54th commit on Tor -- from July 2002,
+      before the release of Tor 0.0.0. This is the new winner of the
+      oldest-bug prize.
+
   o New options for gathering stats safely:
-    - Directories that configure with --enable-dirreq-stats and set
-      "DirReqStatistics 1" write directory request stats to disk every
-      24 hours. As compared to the --enable-geoip-stats flag in 0.2.1.x,
-      there are a few improvements: 1) stats are written to disk exactly
-      every 24 hours; 2) estimated shares of v2 and v3 requests are
-      determined as mean values, not at the end of a measurement period;
-      3) unresolved requests are listed with country code '??';
-      4) directories also measure download times.
-    - Exit nodes that configure with --enable-exit-stats and set
-      "ExitPortStatistics 1" write statistics on the number of exit
-      streams and transferred bytes per port to disk every 24 hours.
-    - Relays that configure with --enable-buffer-stats and set
-      "CellStatistics 1" write statistics to disk every 24 hours on how
-      long cells spend in their circuit queues.
-    - Entry nodes that configure with --enable-entry-stats and set
-      "EntryStatistics 1" write statistics to disk every 24 hours on
-      the rough number and origins of connecting clients.
+    - Directories that set "DirReqStatistics 1" write statistics on
+      directory request to disk every 24 hours. As compared to the
+      --enable-geoip-stats flag in 0.2.1.x, there are a few improvements:
+      1) stats are written to disk exactly every 24 hours; 2) estimated
+      shares of v2 and v3 requests are determined as mean values, not at
+      the end of a measurement period; 3) unresolved requests are listed
+      with country code '??'; 4) directories also measure download times.
+    - Exit nodes that set "ExitPortStatistics 1" write statistics on the
+      number of exit streams and transferred bytes per port to disk every
+      24 hours.
+    - Relays that set "CellStatistics 1" write statistics on how long
+      cells spend in their circuit queues to disk every 24 hours.
+    - Entry nodes that set "EntryStatistics 1" write statistics on the
+      rough number and origins of connecting clients to disk every 24
+      hours.
+    - Relays that write any of the above statistics to disk and set
+      "ExtraInfoStatistics 1" include the past 24 hours of statistics in
+      their extra-info documents.
 
   o Minor features:
     - New --digests command-line switch to output the digests of the
       source files Tor was built with.
     - The "torify" script now uses torsocks where available.
     - The memarea code now uses a sentinel value at the end of each area
-      to make sure nothing writes beyond the end of an area.  This might
+      to make sure nothing writes beyond the end of an area. This might
       help debug some conceivable causes of bug 930.
     - Time and memory units in the configuration file can now be set to
-      fractional units.  For example, "2.5 MB" is now a valid value for
+      fractional units. For example, "2.5 GB" is now a valid value for
       AccountingMax.
     - Certain Tor clients (such as those behind check.torproject.org) may
       want to fetch the consensus in an extra early manner. To enable this
@@ -38,20 +160,50 @@
       setting FetchDirInfoEarly to 1. Previous behavior will stay the same
       as only certain clients who must have this information sooner should
       set this option.
+    - Instead of adding the svn revision to the Tor version string, report
+      the git commit (when we're building from a git checkout).
 
+  o Minor bugfixes:
+    - If any the v3 certs we download are unparseable, we should actually
+      notice the failure so we don't retry indefinitely. Bugfix on
+      0.2.0.x; reported by "rotator".
+    - If the cached cert file is unparseable, warn but don't exit.
+    - Fix possible segmentation fault on directory authorities. Bugfix on
+      0.2.1.14-rc.
+    - When Tor fails to parse a descriptor of any kind, dump it to disk.
+      Might help diagnosing bug 1051.
+
   o Deprecated and removed features:
     - The controller no longer accepts the old obsolete "addr-mappings/"
       or "unregistered-servers-" GETINFO values.
     - Hidden services no longer publish version 0 descriptors, and clients
-      do not request or use version 0 descriptors. However, the authorities
-      still accept and serve version 0 descriptors when contacted by older
-      hidden services/clients.
+      do not request or use version 0 descriptors. However, the old hidden
+      service authorities still accept and serve version 0 descriptors
+      when contacted by older hidden services/clients.
     - The EXTENDED_EVENTS and VERBOSE_NAMES controller features are now
       always on; using them is necessary for correct forward-compatible
       controllers.
-    - Removal of support for .noconnect style addresses.
+    - Remove support for .noconnect style addresses. Nobody was using
+      them, and they provided another avenue for detecting Tor users
+      via application-level web tricks.
 
+  o Packaging changes:
+    - Upgrade Vidalia from 0.1.15 to 0.2.3 in the Windows and OS X
+      installer bundles. See
+      https://trac.vidalia-project.net/browser/vidalia/tags/vidalia-0.2.3/CHANGELOG
+      for details of what's new in Vidalia 0.2.3.
+    - Windows Vidalia Bundle: update Privoxy from 3.0.6 to 3.0.14-beta.
+    - OS X Vidalia Bundle: move to Polipo 1.0.4 with Tor specific
+      configuration file, rather than the old Privoxy.
+    - OS X Vidalia Bundle: Vidalia, Tor, and Polipo are compiled as
+      x86-only for better compatibility with OS X 10.6, aka Snow Leopard.
+    - OS X Tor Expert Bundle: Tor is compiled as x86-only for
+      better compatibility with OS X 10.6, aka Snow Leopard.
+    - OS X Vidalia Bundle: The multi-package installer is now replaced
+      by a simple drag and drop to the /Applications folder. This change
+      occurred with the upgrade to Vidalia 0.2.3.
 
+
 Changes in version 0.2.1.20 - 2009-??-??
   o Major bugfixes:
     - Send circuit or stream sendme cells when our window has decreased
@@ -60,6 +212,9 @@
       patch. Bugfix on the 54th commit on Tor -- from July 2002,
       before the release of Tor 0.0.0. This is the new winner of the
       oldest-bug prize.
+    - Fix a remotely triggerable memory leak when a consensus document
+      contains more than one signature from the same voter. Bugfix on
+      0.2.0.3-alpha.
 
   o New directory authorities:
     - Set up urras (run by Jacob Appelbaum) as the seventh v3 directory
@@ -69,8 +224,43 @@
     - Fix a signed/unsigned compile warning in 0.2.1.19.
     - Fix possible segmentation fault on directory authorities. Bugfix on
       0.2.1.14-rc.
+    - Fix an extremely rare infinite recursion bug that could occur if
+      we tried to log a message after shutting down the log subsystem.
+      Found by Matt Edman. Bugfix on 0.2.0.16-alpha.
+    - Fix an obscure bug where hidden services on 64-bit big-endian
+      systems might mis-read the timestamp in v3 introduce cells, and
+      refuse to connect back to the client. Discovered by "rotor".
+      Bugfix on 0.2.1.6-alpha.
+    - We were triggering a CLOCK_SKEW controller status event whenever
+      we connect via the v2 connection protocol to any relay that has
+      a wrong clock. Instead, we should only inform the controller when
+      it's a trusted authority that claims our clock is wrong. Bugfix
+      on 0.2.0.20-rc; starts to fix bug 1074. Reported by SwissTorExit.
+    - We were telling the controller about CHECKING_REACHABILITY and
+      REACHABILITY_FAILED status events whenever we launch a testing
+      circuit or notice that one has failed. Instead, only tell the
+      controller when we want to inform the user of overall success or
+      overall failure. Bugfix on 0.1.2.6-alpha. Fixes bug 1075. Reported
+      by SwissTorExit.
+    - Don't warn when we're using a circuit that ends with a node
+      excluded in ExcludeExitNodes, but the circuit is not used to access
+      the outside world. This should help fix bug 1090. Bugfix on
+      0.2.1.6-alpha.
+    - Avoid segfault in rare cases when finishing an introduction circuit
+      as a client and finding out that we don't have an introduction key
+      for it. Fixes bug 1073. Reported by Aaron Swartz.
+    - Work around a small memory leak in some versions of OpenSSL that
+      stopped the memory used by the hostname TLS extension from being
+      freed.
 
+  o Minor features:
+    - Add a "getinfo status/accepted-server-descriptor" controller
+      command, which is the recommended way for controllers to learn
+      whether our server descriptor has been successfully received by at
+      least on directory authority. Un-recommend good-server-descriptor
+      getinfo and status events until we have a better design for them.
 
+
 Changes in version 0.2.1.19 - 2009-07-28
   Tor 0.2.1.19 fixes a major bug with accessing and providing hidden
   services on Tor 0.2.1.3-alpha through 0.2.1.18.
@@ -165,6 +355,37 @@
       further bugs for relays on dynamic IP addresses.
 
 
+Changes in version 0.2.0.35 - 2009-06-24
+  o Security fix:
+    - Avoid crashing in the presence of certain malformed descriptors.
+      Found by lark, and by automated fuzzing.
+    - Fix an edge case where a malicious exit relay could convince a
+      controller that the client's DNS question resolves to an internal IP
+      address. Bug found and fixed by "optimist"; bugfix on 0.1.2.8-beta.
+
+  o Major bugfixes:
+    - Finally fix the bug where dynamic-IP relays disappear when their
+      IP address changes: directory mirrors were mistakenly telling
+      them their old address if they asked via begin_dir, so they
+      never got an accurate answer about their new address, so they
+      just vanished after a day. For belt-and-suspenders, relays that
+      don't set Address in their config now avoid using begin_dir for
+      all direct connections. Should fix bugs 827, 883, and 900.
+    - Fix a timing-dependent, allocator-dependent, DNS-related crash bug
+      that would occur on some exit nodes when DNS failures and timeouts
+      occurred in certain patterns. Fix for bug 957.
+
+  o Minor bugfixes:
+    - When starting with a cache over a few days old, do not leak
+      memory for the obsolete router descriptors in it. Bugfix on
+      0.2.0.33; fixes bug 672.
+    - Hidden service clients didn't use a cached service descriptor that
+      was older than 15 minutes, but wouldn't fetch a new one either,
+      because there was already one in the cache. Now, fetch a v2
+      descriptor unless the same descriptor was added to the cache within
+      the last 15 minutes. Fixes bug 997; reported by Marcus Griep.
+
+
 Changes in version 0.2.1.16-rc - 2009-06-20
   Tor 0.2.1.16-rc speeds up performance for fast exit relays, and fixes
   a bunch of minor bugs.

Modified: tor/trunk/Makefile.am
===================================================================
--- tor/trunk/Makefile.am	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/Makefile.am	2009-09-25 17:06:41 UTC (rev 20669)
@@ -58,15 +58,16 @@
 doxygen:
 	doxygen && cd doc/doxygen/latex && make
 
-test:
-	./src/or/test
+test: all
+	./src/test/test
 
 # Avoid strlcpy.c, strlcat.c, tree.h
 check-spaces:
 	./contrib/checkSpace.pl -C                    \
 	        src/common/*.h                        \
 		src/common/[^asO]*.c src/common/address.c \
-		src/or/[^et]*.[ch] src/or/t*.c src/or/eventdns_tor.h
+		src/or/[^et]*.[ch] src/or/t*.c src/or/eventdns_tor.h \
+		src/test/test*.[ch]
 
 check-docs:
 	./contrib/checkOptionDocs.pl

Modified: tor/trunk/configure.in
===================================================================
--- tor/trunk/configure.in	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/configure.in	2009-09-25 17:06:41 UTC (rev 20669)
@@ -4,7 +4,7 @@
 dnl See LICENSE for licensing information
 
 AC_INIT
-AM_INIT_AUTOMAKE(tor, 0.2.2.0-alpha-dev)
+AM_INIT_AUTOMAKE(tor, 0.2.2.3-alpha-dev)
 AM_CONFIG_HEADER(orconfig.h)
 
 AC_CANONICAL_HOST
@@ -85,34 +85,6 @@
      ;;
 esac
 
-AC_ARG_ENABLE(exit-stats,
-     AS_HELP_STRING(--enable-exit-stats, enable code for exits to collect per-port statistics))
-
-if test "$enable_exit_stats" = "yes"; then
-  AC_DEFINE(ENABLE_EXIT_STATS, 1, [Defined if we try to collect per-port statistics on exits])
-fi
-
-AC_ARG_ENABLE(dirreq-stats,
-     AS_HELP_STRING(--enable-dirreq-stats, enable code for directories to collect per-country statistics))
-
-if test "$enable_dirreq_stats" = "yes"; then
-  AC_DEFINE(ENABLE_DIRREQ_STATS, 1, [Defined if we try to collect per-country statistics])
-fi
-
-AC_ARG_ENABLE(buffer-stats,
-     AS_HELP_STRING(--enable-buffer-stats, enable code for relays to collect buffer statistics))
-
-if test "$enable_buffer_stats" = "yes"; then
-  AC_DEFINE(ENABLE_BUFFER_STATS, 1, [Defined if we try to collect buffer statistics])
-fi
-
-AC_ARG_ENABLE(entry-stats,
-     AS_HELP_STRING(--enable-entry-stats, enable code for entry guards to collect per-country statistics))
-
-if test "$enable_entry_stats" = "yes"; then
-  AC_DEFINE(ENABLE_ENTRY_STATS, 1, [Defined if we try to collect per-country statistics])
-fi
-
 AC_ARG_ENABLE(gcc-warnings,
      AS_HELP_STRING(--enable-gcc-warnings, enable verbose warnings))
 
@@ -127,8 +99,10 @@
 AC_PROG_CPP
 AC_PROG_MAKE_SET
 AC_PROG_RANLIB
-AC_PROG_SED
 
+dnl autoconf 2.59 appears not to support AC_PROG_SED
+AC_CHECK_PROG([SED],[sed],[sed],[/bin/false])
+
 AC_PATH_PROG([SHA1SUM], [sha1sum], none)
 AC_PATH_PROG([OPENSSL], [openssl], none)
 
@@ -852,7 +826,7 @@
 
 CPPFLAGS="$CPPFLAGS $TOR_CPPFLAGS_libevent $TOR_CPPFLAGS_openssl $TOR_CPPFLAGS_zlib"
 
-AC_CONFIG_FILES([Makefile tor.spec Doxyfile contrib/tor.sh contrib/torctl contrib/torify contrib/tor.logrotate contrib/Makefile contrib/osx/Makefile contrib/osx/TorBundleDesc.plist contrib/osx/TorBundleInfo.plist contrib/osx/TorDesc.plist contrib/osx/TorInfo.plist contrib/osx/TorStartupDesc.plist src/config/torrc.sample doc/tor.1 src/Makefile doc/Makefile doc/design-paper/Makefile doc/spec/Makefile src/config/Makefile src/common/Makefile src/or/Makefile src/win32/Makefile src/tools/Makefile contrib/suse/Makefile contrib/suse/tor.sh])
+AC_CONFIG_FILES([Makefile tor.spec Doxyfile contrib/tor.sh contrib/torctl contrib/torify contrib/tor.logrotate contrib/Makefile contrib/osx/Makefile contrib/osx/TorBundleDesc.plist contrib/osx/TorBundleInfo.plist contrib/osx/TorDesc.plist contrib/osx/TorInfo.plist contrib/osx/TorStartupDesc.plist src/config/torrc.sample doc/tor.1 src/Makefile doc/Makefile doc/design-paper/Makefile doc/spec/Makefile src/config/Makefile src/common/Makefile src/or/Makefile src/test/Makefile src/win32/Makefile src/tools/Makefile contrib/suse/Makefile contrib/suse/tor.sh])
 AC_OUTPUT
 
 if test -x /usr/bin/perl && test -x ./contrib/updateVersions.pl ; then

Modified: tor/trunk/contrib/auto-naming/README
===================================================================
--- tor/trunk/contrib/auto-naming/README	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/contrib/auto-naming/README	2009-09-25 17:06:41 UTC (rev 20669)
@@ -1,65 +1,6 @@
-=== AUTONAMING FOR TOR ===
-
 Tor directory authorities may maintain a binding of server identities
-(their long term identity key) and nicknames.  In their status documents
-they may for each router they know tell if this is indeed the owner of
-that nickname or not.
+(their long term identity key) and nicknames.
 
-This toolset allows automatic maintaining of a binding list of nicknames
-to identity keys, implementing Tor proposal 123[1].
+The auto-naming scripts have been moved to svn in
+projects/tor-naming/auto-naming/trunk/
 
-The rules are simple:
- - A router claiming to be Bob is named (i.e. added to the binding list)
-   if there currently does not exist a different binding for that
-   nickname, the router has been around for a bit (2 weeks), no other
-   router has used that nickname in a while (1 month).
- - A binding is removed if the server that owns it has not been seen
-   in a long time (6 months).
-
-
-=== REQUIREMENTS ===
-
- * ruby, and its postgres DBI interface (Debian packages: ruby, ruby1.8, libdbi-ruby1.8, libdbd-pg-ruby1.8)
- * postgres (tested with >= 8.1)
- * cron
-
-=== SETUP ===
-
- * copy this tree some place, like into a 'auto-naming' directory in your Tor's
-   data directory
- * create a database and a user, modifying db-config.rb accordingly
- * initialize the database by executing the sql statements in create-db.sql
- * setup a cronjob that feeds the current consensus to the process-consensus
-   script regularly.
- * once the database is sufficiently populated, maybe a month or so after the
-   previous step, setup a cronjob to regularly build the binding list using
-   the build-approved-routers script.  You probably want to append a manually
-   managed list of rejections to that file and give it to tor as its
-   "approved-routers" file.
-   The Sample-Makefile and Sample-crontab demonstrate the method used at tor26.
-
-
-1. https://tor-svn.freehaven.net/svn/tor/trunk/doc/spec/proposals/123-autonaming.txt
-
-
-
-
-Copyright (c) 2007 Peter Palfrader
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.

Modified: tor/trunk/contrib/cross.sh
===================================================================
--- tor/trunk/contrib/cross.sh	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/contrib/cross.sh	2009-09-25 17:06:41 UTC (rev 20669)
@@ -185,7 +185,7 @@
 then
 ${HOST_TRIPLET}strip \
 	src/or/tor \
-	src/or/test \
+	src/test/test \
 	src/tools/tor-resolve
 fi
 

Modified: tor/trunk/contrib/directory-archive/fetch-all
===================================================================
--- tor/trunk/contrib/directory-archive/fetch-all	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/contrib/directory-archive/fetch-all	2009-09-25 17:06:41 UTC (rev 20669)
@@ -30,8 +30,8 @@
 DIRSERVERS="$DIRSERVERS 86.59.21.38:80"		# tor26
 DIRSERVERS="$DIRSERVERS 128.31.0.34:9031"	# moria1
 DIRSERVERS="$DIRSERVERS 128.31.0.34:9032"	# moria2
-#DIRSERVERS="$DIRSERVERS 140.247.60.64:80"	# lefkada
 DIRSERVERS="$DIRSERVERS 194.109.206.212:80"	# dizum
+
 DATEDIR=$(date "+%Y/%m/%d")
 TIME=$(date "+%Y%m%d-%H%M%S")
 

Modified: tor/trunk/contrib/tor-mingw.nsi.in
===================================================================
--- tor/trunk/contrib/tor-mingw.nsi.in	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/contrib/tor-mingw.nsi.in	2009-09-25 17:06:41 UTC (rev 20669)
@@ -9,7 +9,7 @@
 !include "FileFunc.nsh"
 !insertmacro GetParameters
   
-!define VERSION "0.2.2.0-alpha-dev"
+!define VERSION "0.2.2.3-alpha-dev"
 !define INSTALLER "tor-${VERSION}-win32.exe"
 !define WEBSITE "https://www.torproject.org/"
 !define LICENSE "LICENSE"

Modified: tor/trunk/contrib/torify.in
===================================================================
--- tor/trunk/contrib/torify.in	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/contrib/torify.in	2009-09-25 17:06:41 UTC (rev 20669)
@@ -3,57 +3,52 @@
 # Wrapper script for use of the tsocks(8) transparent socksification library
 # See the tsocks(1) and torify(1) manpages.
 
-# Copyright (c) 2004, 2006 Peter Palfrader
+# Copyright (c) 2004, 2006, 2009 Peter Palfrader
 # Modified by Jacob Appelbaum <jacob at appelbaum.net> April 16th 2006
 # May be distributed under the same terms as Tor itself
 
+# taken from Debian's Developer's Reference, 6.4
+pathfind() {
+	OLDIFS="$IFS"
+	IFS=:
+	for p in $PATH; do
+		if [ -x "$p/$*" ]; then
+			IFS="$OLDIFS"
+			return 0
+		fi
+	done
+	IFS="$OLDIFS"
+	return 1
+}
 
-# Define and ensure we have tsocks
-# XXX: what if we don't have which?
-TORSOCKS="`which torsocks`"
-TSOCKS="`which tsocks`"
-PROG=""
-if [ ! -x "$TSOCKS" ]
-then
-	echo "$0: Can't find tsocks in PATH. Perhaps you haven't installed it?" >&2
-else
-	PROG=$TSOCKS
-fi
-if [ ! -x "$TORSOCKS" ]
-then
-	echo "$0: Can't find torsocks in PATH. Perhaps you haven't installed it?" >&2
-else
-	PROG=$TORSOCKS
-fi
-
-if [ ! -x "$PROG" ]
-then
-	echo "$0: Can't find the required tor helpers in our PATH. Perhaps you haven't installed them?" >&2
-	exit 1;
-fi
-
 # Check for any argument list
-if [ "$#" = 0 ]
-then
+if [ "$#" = 0 ]; then
 	echo "Usage: $0 [-hv] <command> [<options>...]" >&2
 	exit 1
 fi
-if [ "$#" = 1 ] && ( [ "$1" = "-h" ] || [ "$1" = "--help" ] )
-then
+
+if [ "$#" = 1 ] && ( [ "$1" = "-h" ] || [ "$1" = "--help" ] ); then
 	echo "Usage: $0 [-hv] <command> [<options>...]"
 	exit 0
 fi
 
-if [ "$1" = "-v" ] || [ "$1" = "--verbose" ]
-then
-	echo "We're armed with the following tsocks: $TSOCKS"
-	echo "We're armed with the following torsocks: $TORSOCKS"
-	echo "We're attempting to use $PROG for all tor action."
+if [ "$1" = "-v" ] || [ "$1" = "--verbose" ]; then
+	verbose=1
 	shift 1
+else
+	verbose=0
 fi
 
-if [ "$PROG" = "$TSOCKS" ]
-then
+if pathfind torsocks; then
+	! [ "$verbose" -ge 1 ] || echo "Using torsocks as socksifier." >&2
+
+	exec torsocks "$@"
+	echo "$0: Failed to exec torsocks $@" >&2
+	exit 1
+
+elif pathfind tsocks; then
+	! [ "$verbose" -ge 1 ] || echo "Using tsocks as socksifier." >&2
+
 	# Define our tsocks config file
 	TSOCKS_CONF_FILE="/etc/tor/tor-tsocks.conf"
 	export TSOCKS_CONF_FILE
@@ -61,7 +56,7 @@
 	# Check that we've got a tsocks config file
 	if [ -r "$TSOCKS_CONF_FILE" ]
 	then
-		echo "WARNING: tsocks is known to leak DNS and UDP data." >&2
+		echo "WARNING: tsocks is known to leak DNS and UDP data.  If you had torsocks we would use that." >&2
 		exec tsocks "$@"
 		echo "$0: Failed to exec tsocks $@" >&2
 		exit 1
@@ -69,8 +64,8 @@
 		echo "$0: Missing tsocks configuration file \"$TSOCKS_CONF_FILE\"." >&2
 		exit 1
 	fi
+
+else
+	echo "$0: Can't find either tsocks or torsocks in your PATH. Perhaps you haven't installed either?" >&2
+	exit 1
 fi
-if [ "$PROG" = "$TORSOCKS" ]
-then
-	exec torsocks "$@"
-fi

Modified: tor/trunk/debian/changelog
===================================================================
--- tor/trunk/debian/changelog	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/debian/changelog	2009-09-25 17:06:41 UTC (rev 20669)
@@ -1,3 +1,39 @@
+tor (0.2.2.3-alpha-1) experimental; urgency=low
+
+  * New upstream version.
+
+ -- Peter Palfrader <weasel at debian.org>  Wed, 23 Sep 2009 10:27:40 +0200
+
+tor (0.2.2.2-alpha-1) experimental; urgency=low
+
+  * New upstream version.
+  * The files src/common/common_sha1.i src/or/or_sha1.i get changed
+    during the build - they contain the checksums of the individual
+    files that end up in the binary.  Of couse changes only end up
+    in the debian diff.gz after building a second time in the same
+    directory.  So, remove those files in clean to get both a cleaner
+    diff.gz and idempotent builds.
+  * If we have a debian/micro-revision.i, replace the one in src/or
+    with our copy so that this will be the revision that ends up in
+    the binary.  This is an informational only version string, but
+    it'd be kinda nice if it was (more) accurate nonetheless.
+    .
+    Of course this won't help if people manually patch around but
+    it's still preferable to claiming we are exactly upstream's source.
+    .
+    If we are building directly out of a git tree, update
+    debian/micro-revision.i in the clean target.
+
+ -- Peter Palfrader <weasel at debian.org>  Mon, 21 Sep 2009 14:51:20 +0200
+
+tor (0.2.2.1-alpha-1) experimental; urgency=low
+
+  * New upstream version.
+  * Forward port patches/03_tor_manpage_in_section_8.dpatch.
+  * Forward port patches/06_add_compile_time_defaults.dpatch.
+
+ -- Peter Palfrader <weasel at debian.org>  Thu, 03 Sep 2009 15:10:26 +0200
+
 tor (0.2.1.19-1) unstable; urgency=low
 
   * New upstream version.

Modified: tor/trunk/debian/patches/03_tor_manpage_in_section_8.dpatch
===================================================================
--- tor/trunk/debian/patches/03_tor_manpage_in_section_8.dpatch	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/debian/patches/03_tor_manpage_in_section_8.dpatch	2009-09-25 17:06:41 UTC (rev 20669)
@@ -26,20 +26,21 @@
 diff -urNad tor-0.1.1.5/contrib/torify.1 /tmp/dpep.fOA3Mm/tor-0.1.1.5/contrib/torify.1
 --- tor-0.1.1.5/contrib/torify.1
 +++ /tmp/dpep.fOA3Mm/tor-0.1.1.5/contrib/torify.1
-@@ -18,6 +18,6 @@
+@@ -35,7 +35,7 @@
  to suid binaries.
  
  .SH SEE ALSO
 -.BR tor (1),
 +.BR tor (8),
  .BR tor-resolve (1),
+ .BR torsocks (1),
  .BR tsocks (1),
 diff -urNad tor-0.1.1.5/doc/tor.1.in /tmp/dpep.fOA3Mm/tor-0.1.1.5/doc/tor.1.in
 --- tor-0.1.1.5/doc/tor.1.in
 +++ /tmp/dpep.fOA3Mm/tor-0.1.1.5/doc/tor.1.in
 @@ -1,4 +1,4 @@
--.TH TOR 1 "January 2009" "TOR"
-+.TH TOR 8 "January 2009" "TOR"
+-.TH TOR 1 "August 2009" "TOR"
++.TH TOR 8 "August 2009" "TOR"
  .SH NAME
  tor \- The second-generation onion router
  .SH SYNOPSIS

Modified: tor/trunk/debian/patches/06_add_compile_time_defaults.dpatch
===================================================================
--- tor/trunk/debian/patches/06_add_compile_time_defaults.dpatch	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/debian/patches/06_add_compile_time_defaults.dpatch	2009-09-25 17:06:41 UTC (rev 20669)
@@ -23,9 +23,9 @@
 exit 0
 
 @DPATCH@
-diff -urNad tor-trunk~/src/or/config.c tor-trunk/src/or/config.c
---- tor-trunk~/src/or/config.c	2009-01-18 01:47:33.000000000 +0100
-+++ tor-trunk/src/or/config.c	2009-02-05 00:25:17.614844812 +0100
+diff -urNad tor~/src/or/config.c tor/src/or/config.c
+--- tor~/src/or/config.c	2009-09-03 15:05:41.000000000 +0200
++++ tor/src/or/config.c	2009-09-03 15:09:37.662104166 +0200
 @@ -12,6 +12,7 @@
  #define CONFIG_PRIVATE
  
@@ -34,16 +34,17 @@
  #ifdef MS_WINDOWS
  #include <shlobj.h>
  #endif
-@@ -711,6 +712,8 @@
- #if defined(HAVE_EVENT_GET_VERSION) && defined(HAVE_EVENT_GET_METHOD)
- static void check_libevent_version(const char *m, int server);
- #endif
+@@ -717,6 +718,9 @@
+ static void init_libevent(void);
+ static int opt_streq(const char *s1, const char *s2);
+ 
 +static int debian_running_as_debiantor();
 +static int debian_config_fix_defaults();
- 
++
  /** Magic value for or_options_t. */
  #define OR_OPTIONS_MAGIC 9090909
-@@ -3917,6 +3920,9 @@
+ 
+@@ -4086,6 +4090,9 @@
    char *command_arg = NULL;
    char *errmsg=NULL;
  
@@ -53,7 +54,7 @@
    if (argv) { /* first time we're called. save command line args */
      backup_argv = argv;
      backup_argc = argc;
-@@ -5307,3 +5313,62 @@
+@@ -5304,3 +5311,62 @@
    return 0;
  }
  

Modified: tor/trunk/debian/rules
===================================================================
--- tor/trunk/debian/rules	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/debian/rules	2009-09-25 17:06:41 UTC (rev 20669)
@@ -82,6 +82,7 @@
 
 build-stamp:  config.status
 	dh_testdir
+	! [ debian/micro-revision.i ] || cp debian/micro-revision.i src/or/micro-revision.i
 
 	$(MAKE)
 	@echo
@@ -129,9 +130,16 @@
 	dh_testdir
 	dh_testroot
 	rm -f build-stamp
+	rm -f src/common/common_sha1.i src/or/or_sha1.i
+	rm -f src/or/micro-revision.i
 
 	[ ! -f Makefile ] || $(MAKE) distclean
 
+	# Normally the .deb wouldn't ship with a ../.git
+	if [ -d .git ] && which git >/dev/null; then \
+		echo "\"`git rev-parse --short=16 HEAD`\"" > "debian/micro-revision.i" ; \
+	fi
+
 	dh_clean
 
 install: build

Modified: tor/trunk/doc/HACKING
===================================================================
--- tor/trunk/doc/HACKING	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/doc/HACKING	2009-09-25 17:06:41 UTC (rev 20669)
@@ -3,11 +3,8 @@
 
 0.0 The buildbot.
 
-  http://tor-buildbot.freehaven.net:8010/
+  https://buildbot.vidalia-project.net/one_line_per_build
 
-  - Down because nickm isn't running services at home any more. ioerror says
-    he will resurrect it.
-
 0.1. Useful command-lines that are non-trivial to reproduce but can
 help with tracking bugs or leaks.
 
@@ -29,7 +26,7 @@
 
   make clean
   make CFLAGS='-g -fprofile-arcs -ftest-coverage'
-  ./src/or/test
+  ./src/test/test
   cd src/common; gcov *.[ch]
   cd ../or; gcov *.[ch]
 

Modified: tor/trunk/doc/TODO.022
===================================================================
--- tor/trunk/doc/TODO.022	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/doc/TODO.022	2009-09-25 17:06:41 UTC (rev 20669)
@@ -58,10 +58,10 @@
     - 158: microdescriptors
       o Revise proposal
       - Implement
-    - 160: list bandwidth in consensus
-RNM?  . Finish proposal
-      - and actually set it reasonably
-      - and actually use it.
+    o 160: list bandwidth in consensus
+      o Finish proposal
+      o and actually set it reasonably
+      o and actually use it.
 
   - Proposals to improve and implement if not broken
     D IPv6 support.  (Parts of 117, but figure out how to handle DNS

Modified: tor/trunk/doc/spec/address-spec.txt
===================================================================
--- tor/trunk/doc/spec/address-spec.txt	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/doc/spec/address-spec.txt	2009-09-25 17:06:41 UTC (rev 20669)
@@ -33,10 +33,13 @@
   "www.google.com.foo.exit=64.233.161.99.foo.exit" to speed subsequent
   lookups.
 
+  The .exit notation is disabled by default as of Tor 0.2.2.1-alpha, due
+  to potential application-level attacks.
+
   EXAMPLES:
      www.example.com.exampletornode.exit
 
-        Connect to www.example.com from the node called "exampletornode."
+        Connect to www.example.com from the node called "exampletornode".
 
      exampletornode.exit
 

Modified: tor/trunk/doc/spec/control-spec.txt
===================================================================
--- tor/trunk/doc/spec/control-spec.txt	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/doc/spec/control-spec.txt	2009-09-25 17:06:41 UTC (rev 20669)
@@ -557,6 +557,7 @@
     "status/circuit-established"
     "status/enough-dir-info"
     "status/good-server-descriptor"
+    "status/accepted-server-descriptor"
     "status/..."
       These provide the current internal Tor values for various Tor
       states. See Section 4.1.10 for explanations. (Only a few of the
@@ -1254,20 +1255,26 @@
      CLOCK_SKEW
        SKEW="+" / "-" SECONDS
        MIN_SKEW="+" / "-" SECONDS.
-       SOURCE="DIRSERV:IP:Port" / "NETWORKSTATUS:IP:PORT" / "CONSENSUS"
+       SOURCE="DIRSERV:" IP ":" Port /
+              "NETWORKSTATUS:" IP ":" Port /
+              "OR:" IP ":" Port /
+              "CONSENSUS"
          If "SKEW" is present, it's an estimate of how far we are from the
          time declared in the source.  (In other words, if we're an hour in
          the past, the value is -3600.)  "MIN_SKEW" is present, it's a lower
          bound.  If the source is a DIRSERV, we got the current time from a
          connection to a dirserver.  If the source is a NETWORKSTATUS, we
          decided we're skewed because we got a v2 networkstatus from far in
-         the future.  If the source is CONSENSUS, we decided we're skewed
-         because we got a networkstatus consensus from the future.
+         the future.  If the source is OR, the skew comes from a NETINFO
+         cell from a connection to another relay.  If the source is
+         CONSENSUS, we decided we're skewed because we got a networkstatus
+         consensus from the future.
 
-         {Controllers may want to warn the user if the skew is high, or if
-         multiple skew messages appear at severity WARN.  Controllers
-         shouldn't blindly adjust the clock, since the more accurate source
-         of skew info (DIRSERV) is currently unauthenticated.}
+         {Tor should send this message to controllers when it thinks the
+         skew is so high that it will interfere with proper Tor operation.
+         Controllers shouldn't blindly adjust the clock, since the more
+         accurate source of skew info (DIRSERV) is currently
+         unauthenticated.}
 
      BAD_LIBEVENT
      "METHOD=" libevent method
@@ -1481,18 +1488,39 @@
        We successfully uploaded our server descriptor to at least one
        of the directory authorities, with no complaints.
 
-       {This event could affect the controller's idea of server status, but
-       the controller should not interrupt the user to tell them so.}
+       {Originally, the goal of this event was to declare "every authority
+       has accepted the descriptor, so there will be no complaints
+       about it." But since some authorities might be offline, it's
+       harder to get certainty than we had thought. As such, this event
+       is equivalent to ACCEPTED_SERVER_DESCRIPTOR below. Controllers
+       should just look at ACCEPTED_SERVER_DESCRIPTOR and should ignore
+       this event for now.}
 
+     SERVER_DESCRIPTOR_STATUS
+     "STATUS=" "LISTED" / "UNLISTED"
+       We just got a new networkstatus consensus, and whether we're in
+       it or not in it has changed. Specifically, status is "listed"
+       if we're listed in it but previous to this point we didn't know
+       we were listed in a consensus; and status is "unlisted" if we
+       thought we should have been listed in it (e.g. we were listed in
+       the last one), but we're not.
+
+       {Moving from listed to unlisted is not necessarily cause for
+       alarm. The relay might have failed a few reachability tests,
+       or the Internet might have had some routing problems. So this
+       feature is mainly to let relay operators know when their relay
+       has successfully been listed in the consensus.}
+
+       [Not implemented yet. We should do this in 0.2.2.x. -RD]
+
      NAMESERVER_STATUS
      "NS=addr"
      "STATUS=" "UP" / "DOWN"
      "ERR=" message
         One of our nameservers has changed status.
-        // actually notice
 
-       {This event could affect the controller's idea of server status, but
-       the controller should not interrupt the user to tell them so.}
+        {This event could affect the controller's idea of server status, but
+        the controller should not interrupt the user to tell them so.}
 
      NAMESERVER_ALL_DOWN
         All of our nameservers have gone down.

Modified: tor/trunk/doc/spec/dir-spec.txt
===================================================================
--- tor/trunk/doc/spec/dir-spec.txt	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/doc/spec/dir-spec.txt	2009-09-25 17:06:41 UTC (rev 20669)
@@ -593,7 +593,7 @@
 
    "allow-single-hop-exits"
 
-       [At most one.]
+       [At most once.]
 
        Present only if the router allows single-hop circuits to make exit
        connections.  Most Tor servers do not support this: this is
@@ -641,6 +641,200 @@
         "geoip-start" is the time at which we began collecting geoip
         statistics.
 
+    "dirreq-stats-end" YYYY-MM-DD HH:MM:SS (NSEC s) NL
+        [At most once.]
+
+        YYYY-MM-DD HH:MM:SS defines the end of the included measurement
+        interval of length NSEC seconds (86400 seconds by default).
+
+        A "dirreq-stats-end" line, as well as any other "dirreq-*" line,
+        is only added when the relay has opened its Dir port and after 24
+        hours of measuring directory requests.
+
+    "dirreq-v2-ips" CC=N,CC=N,... NL
+        [At most once.]
+    "dirreq-v3-ips" CC=N,CC=N,... NL
+        [At most once.]
+
+        List of mappings from two-letter country codes to the number of
+        unique IP addresses that have connected from that country to
+        request a v2/v3 network status, rounded up to the nearest multiple
+        of 8. Only those IP addresses are counted that the directory can
+        answer with a 200 OK status code.
+
+    "dirreq-v2-reqs" CC=N,CC=N,... NL
+        [At most once.]
+    "dirreq-v3-reqs" CC=N,CC=N,... NL
+        [At most once.]
+
+        List of mappings from two-letter country codes to the number of
+        requests for v2/v3 network statuses from that country, rounded up
+        to the nearest multiple of 8. Only those requests are counted that
+        the directory can answer with a 200 OK status code.
+
+    "dirreq-v2-share" num% NL
+        [At most once.]
+    "dirreq-v3-share" num% NL
+        [At most once.]
+
+        The share of v2/v3 network status requests that the directory
+        expects to receive from clients based on its advertised bandwidth
+        compared to the overall network bandwidth capacity. Shares are
+        formatted in percent with two decimal places. Shares are
+        calculated as means over the whole 24-hour interval.
+
+    "dirreq-v2-resp" status=num,... NL
+        [At most once.]
+    "dirreq-v3-resp" status=nul,... NL
+        [At most once.]
+
+        List of mappings from response statuses to the number of requests
+        for v2/v3 network statuses that were answered with that response
+        status, rounded up to the nearest multiple of 4. Only response
+        statuses with at least 1 response are reported. New response
+        statuses can be added at any time. The current list of response
+        statuses is as follows:
+
+        "ok": a network status request is answered; this number
+           corresponds to the sum of all requests as reported in
+           "dirreq-v2-reqs" or "dirreq-v3-reqs", respectively, before
+           rounding up.
+        "not-enough-sigs: a version 3 network status is not signed by a
+           sufficient number of requested authorities.
+        "unavailable": a requested network status object is unavailable.
+        "not-found": a requested network status is not found.
+        "not-modified": a network status has not been modified since the
+           If-Modified-Since time that is included in the request.
+        "busy": the directory is busy.
+
+    "dirreq-v2-direct-dl" key=val,... NL
+        [At most once.]
+    "dirreq-v3-direct-dl" key=val,... NL
+        [At most once.]
+    "dirreq-v2-tunneled-dl" key=val,... NL
+        [At most once.]
+    "dirreq-v3-tunneled-dl" key=val,... NL
+        [At most once.]
+
+        List of statistics about possible failures in the download process
+        of v2/v3 network statuses. Requests are either "direct"
+        HTTP-encoded requests over the relay's directory port, or
+        "tunneled" requests using a BEGIN_DIR cell over the relay's OR
+        port. The list of possible statistics can change, and statistics
+        can be left out from reporting. The current list of statistics is
+        as follows:
+
+        Successful downloads and failures:
+
+        "complete": a client has finished the download successfully.
+        "timeout": a download did not finish within 10 minutes after
+           starting to send the response.
+        "running": a download is still running at the end of the
+           measurement period for less than 10 minutes after starting to
+           send the response.
+
+        Download times:
+
+        "min", "max": smallest and largest measured bandwidth in B/s.
+        "d[1-4,6-9]": 1st to 4th and 6th to 9th decile of measured
+           bandwidth in B/s. For a given decile i, i/10 of all downloads
+           had a smaller bandwidth than di, and (10-i)/10 of all downloads
+           had a larger bandwidth than di.
+        "q[1,3]": 1st and 3rd quartile of measured bandwidth in B/s. One
+           fourth of all downloads had a smaller bandwidth than q1, one
+           fourth of all downloads had a larger bandwidth than q3, and the
+           remaining half of all downloads had a bandwidth between q1 and
+           q3.
+        "md": median of measured bandwidth in B/s. Half of the downloads
+           had a smaller bandwidth than md, the other half had a larger
+           bandwidth than md.
+
+    "entry-stats-end" YYYY-MM-DD HH:MM:SS (NSEC s) NL
+        [At most once.]
+
+        YYYY-MM-DD HH:MM:SS defines the end of the included measurement
+        interval of length NSEC seconds (86400 seconds by default).
+
+        An "entry-stats-end" line, as well as any other "entry-*"
+        line, is first added after the relay has been running for at least
+        24 hours.
+
+    "entry-ips" CC=N,CC=N,... NL
+        [At most once.]
+
+        List of mappings from two-letter country codes to the number of
+        unique IP addresses that have connected from that country to the
+        relay and which are no known other relays, rounded up to the
+        nearest multiple of 8.
+
+    "cell-stats-end" YYYY-MM-DD HH:MM:SS (NSEC s) NL
+        [At most once.]
+
+        YYYY-MM-DD HH:MM:SS defines the end of the included measurement
+        interval of length NSEC seconds (86400 seconds by default).
+
+        A "cell-stats-end" line, as well as any other "cell-*" line,
+        is first added after the relay has been running for at least 24
+        hours.
+
+    "cell-processed-cells" num,...,num NL
+        [At most once.]
+
+        Mean number of processed cells per circuit, subdivided into
+        deciles of circuits by the number of cells they have processed in
+        descending order from loudest to quietest circuits.
+
+    "cell-queued-cells" num,...,num NL
+        [At most once.]
+
+        Mean number of cells contained in queues by circuit decile. These
+        means are calculated by 1) determining the mean number of cells in
+        a single circuit between its creation and its termination and 2)
+        calculating the mean for all circuits in a given decile as
+        determined in "cell-processed-cells". Numbers have a precision of
+        two decimal places.
+
+    "cell-time-in-queue" num,...,num NL
+        [At most once.]
+
+        Mean time cells spend in circuit queues in milliseconds. Times are
+        calculated by 1) determining the mean time cells spend in the
+        queue of a single circuit and 2) calculating the mean for all
+        circuits in a given decile as determined in
+        "cell-processed-cells".
+
+    "cell-circuits-per-decile" num NL
+        [At most once.]
+
+        Mean number of circuits that are included in any of the deciles,
+        rounded up to the next integer.
+
+    "exit-stats-end" YYYY-MM-DD HH:MM:SS (NSEC s) NL
+        [At most once.]
+
+        YYYY-MM-DD HH:MM:SS defines the end of the included measurement
+        interval of length NSEC seconds (86400 seconds by default).
+
+        An "exit-stats-end" line, as well as any other "exit-*" line, is
+        first added after the relay has been running for at least 24 hours
+        and only if the relay permits exiting (where exiting to a single
+        port and IP address is sufficient).
+
+    "exit-kibibytes-written" port=N,port=N,... NL
+        [At most once.]
+    "exit-kibibytes-read" port=N,port=N,... NL
+        [At most once.]
+
+        List of mappings from ports to the number of kibibytes that the
+        relay has written to or read from exit connections to that port,
+        rounded up to the next full kibibyte.
+
+    "exit-streams-opened" port=N,port=N,... NL
+        [At most once.]
+
+        List of mappings from ports to the number of opened exit streams
+        to that port, rounded up to the nearest multiple of 4.
+
     "router-signature" NL Signature NL
         [At end, exactly once.]
 
@@ -797,7 +991,7 @@
    documents are described in section XXX below.
 
    Status documents contain a preamble, an authority section, a list of
-   router status entries, and one more footers signature, in that order.
+   router status entries, and one or more footer signature, in that order.
 
    Unlike other formats described above, a SP in these documents must be a
    single space character (hex 20).
@@ -904,7 +1098,21 @@
         enough votes were counted for the consensus for an authoritative
         opinion to have been formed about their status.
 
+    "params" SP [Parameters] NL
 
+        [At most once]
+
+        Parameter ::= Keyword '=' Int32
+        Int32 ::= A decimal integer between -2147483648 and 2147483647.
+        Parameters ::= Parameter | Parameters SP Parameter
+
+        The parameters list, if present, contains a space-separated list of
+        key-value pairs, sorted in lexical order by their keyword.  Each
+        parameter has its own meaning.
+
+        (Only included when the vote is generated with consensus-method 7 or
+        later.)
+
    The authority section of a vote contains the following items, followed
    in turn by the authority's current key certificate:
 
@@ -1212,6 +1420,10 @@
 
      Known-flags is the union of all flags known by any voter.
 
+     Entries are given on the "params" line for every keyword on which any
+     authority voted.  The values given are the low-median of all votes on
+     that keyword.
+
     "client-versions" and "server-versions" are sorted in ascending
      order; A version is recommended in the consensus if it is recommended
      by more than half of the voting authorities that included a
@@ -1279,6 +1491,9 @@
           a router, the authorities produce a consensus containing a 
           Bandwidth= keyword equal to the median of the Measured= votes.
 
+        * If consensus-method 7 or later is in use, the params line is
+          included in the output.
+
      The signatures at the end of a consensus document are sorted in
      ascending order by identity digest.
 

Modified: tor/trunk/doc/spec/proposals/000-index.txt
===================================================================
--- tor/trunk/doc/spec/proposals/000-index.txt	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/doc/spec/proposals/000-index.txt	2009-09-25 17:06:41 UTC (rev 20669)
@@ -86,7 +86,9 @@
 163  Detecting whether a connection comes from a client [OPEN]
 164  Reporting the status of server votes [OPEN]
 165  Easy migration for voting authority sets [OPEN]
-166  Including Network Statistics in Extra-Info Documents [OPEN]
+166  Including Network Statistics in Extra-Info Documents [ACCEPTED]
+167  Vote on network parameters in consensus [CLOSED]
+168  Reduce default circuit window [OPEN]
 
 
 Proposals by status:
@@ -114,7 +116,7 @@
    163  Detecting whether a connection comes from a client [for 0.2.2]
    164  Reporting the status of server votes [for 0.2.2]
    165  Easy migration for voting authority sets
-   166  Including Network Statistics in Extra-Info Documents [for 0.2.2]
+   168  Reduce default circuit window [for 0.2.2]
  ACCEPTED:
    110  Avoiding infinite length circuits [for 0.2.1.x] [in 0.2.1.3-alpha]
    117  IPv6 exits [for 0.2.1.x]
@@ -122,6 +124,7 @@
    140  Provide diffs between consensuses [for 0.2.2.x]
    147  Eliminate the need for v2 directories in generating v3 directories [for 0.2.1.x]
    157  Make certificate downloads specific [for 0.2.1.x]
+   166  Including Network Statistics in Extra-Info Documents [for 0.2.2]
  META:
    000  Index of Tor Proposals
    001  The Tor Proposal Process
@@ -157,6 +160,7 @@
    148  Stream end reasons from the client side should be uniform [in 0.2.1.9-alpha]
    150  Exclude Exit Nodes from a circuit [in 0.2.1.3-alpha]
    152  Optionally allow exit from single-hop circuits [in 0.2.1.6-alpha]
+   167  Vote on network parameters in consensus [in 0.2.2]
  SUPERSEDED:
    112  Bring Back Pathlen Coin Weight
    113  Simplifying directory authority administration

Modified: tor/trunk/doc/spec/proposals/151-path-selection-improvements.txt
===================================================================
--- tor/trunk/doc/spec/proposals/151-path-selection-improvements.txt	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/doc/spec/proposals/151-path-selection-improvements.txt	2009-09-25 17:06:41 UTC (rev 20669)
@@ -2,13 +2,13 @@
 Title: Improving Tor Path Selection
 Author: Fallon Chen, Mike Perry
 Created: 5-Jul-2008
-Status: Draft
+Status: Implemented
 
 Overview
 
   The performance of paths selected can be improved by adjusting the
   CircuitBuildTimeout and avoiding failing guard nodes. This proposal
-  describes a method of tracking buildtime statistics at the client, and 
+  describes a method of tracking buildtime statistics at the client, and
   using those statistics to adjust the CircuitBuildTimeout.
 
 Motivation
@@ -20,121 +20,123 @@
 
 Implementation
 
-  Storing Build Times
+  Gathering Build Times
 
-    Circuit build times will be stored in the circular array
-    'circuit_build_times' consisting of uint16_t elements as milliseconds.
-    The total size of this array will be based on the number of circuits
+    Circuit build times are stored in the circular array
+    'circuit_build_times' consisting of uint32_t elements as milliseconds.
+    The total size of this array is based on the number of circuits
     it takes to converge on a good fit of the long term distribution of
     the circuit builds for a fixed link. We do not want this value to be
     too large, because it will make it difficult for clients to adapt to
     moving between different links.
 
-    From our initial observations, this value appears to be on the order 
-    of 1000, but will be configurable in a #define NCIRCUITS_TO_OBSERVE.
-    The exact value for this #define will be determined by performing
-    goodness of fit tests using measurments obtained from the shufflebt.py
-    script from TorFlow.
- 
+    From our observations, the minimum value for a reasonable fit appears
+    to be on the order of 500 (MIN_CIRCUITS_TO_OBSERVE). However, to keep
+    a good fit over the long term, we store 5000 most recent circuits in
+    the array (NCIRCUITS_TO_OBSERVE).
+
+    The Tor client will build test circuits at a rate of one per
+    minute (BUILD_TIMES_TEST_FREQUENCY) up to the point of
+    MIN_CIRCUITS_TO_OBSERVE. This allows a fresh Tor to have
+    a CircuitBuildTimeout estimated within 8 hours after install,
+    upgrade, or network change (see below).
+
   Long Term Storage
 
-    The long-term storage representation will be implemented by storing a 
-    histogram with BUILDTIME_BIN_WIDTH millisecond buckets (default 50) when 
-    writing out the statistics to disk. The format of this histogram on disk 
-    is yet to be finalized, but it will likely be of the format 
-    'CircuitBuildTime <bin> <count>', with the total specified as 
-    'TotalBuildTimes <total>'
+    The long-term storage representation is implemented by storing a
+    histogram with BUILDTIME_BIN_WIDTH millisecond buckets (default 50) when
+    writing out the statistics to disk. The format this takes in the
+    state file is 'CircuitBuildTime <bin-ms> <count>', with the total
+    specified as 'TotalBuildTimes <total>'
     Example:
 
     TotalBuildTimes 100
-    CircuitBuildTimeBin 1 50
-    CircuitBuildTimeBin 2 25
-    CircuitBuildTimeBin 3 13
+    CircuitBuildTimeBin 25 50
+    CircuitBuildTimeBin 75 25
+    CircuitBuildTimeBin 125 13
     ...
 
-    Reading the histogram in will entail multiplying each bin by the 
-    BUILDTIME_BIN_WIDTH and then inserting <count> values into the 
-    circuit_build_times array each with the value of
-    <bin>*BUILDTIME_BIN_WIDTH. In order to evenly distribute the 
-    values in the circular array, a form of index skipping must
-    be employed. Values from bin #N with bin count C and total T
-    will occupy indexes specified by N+((T/C)*k)-1, where k is the
-    set of integers ranging from 0 to C-1.
+    Reading the histogram in will entail inserting <count> values
+    into the circuit_build_times array each with the value of
+    <bin-ms> milliseconds. In order to evenly distribute the values
+    in the circular array, the Fisher-Yates shuffle will be performed
+    after reading values from the bins.
 
-    For example, this would mean that the values from bin 1 would
-    occupy indexes 1+(100/50)*k-1, or 0, 2, 4, 6, 8, 10 and so on.
-    The values for bin 2 would occupy positions 1, 5, 9, 13. Collisions
-    will be inserted at the first empty position in the array greater 
-    than the selected index (which may requiring looping around the 
-    array back to index 0).
-
   Learning the CircuitBuildTimeout
 
     Based on studies of build times, we found that the distribution of
-    circuit buildtimes appears to be a Pareto distribution. 
+    circuit buildtimes appears to be a Frechet distribution. However,
+    estimators and quantile functions of the Frechet distribution are
+    difficult to work with and slow to converge. So instead, since we
+    are only interested in the accuracy of the tail, we approximate
+    the tail of the distribution with a Pareto curve starting at
+    the mode of the circuit build time sample set.
 
     We will calculate the parameters for a Pareto distribution
     fitting the data using the estimators at
     http://en.wikipedia.org/wiki/Pareto_distribution#Parameter_estimation.
 
-    The timeout itself will be calculated by solving the CDF for the 
-    a percentile cutoff BUILDTIME_PERCENT_CUTOFF. This value
-    represents the percentage of paths the Tor client will accept out of
-    the total number of paths. We have not yet determined a good
-    cutoff for this mathematically, but 85% seems a good choice for now.
+    The timeout itself is calculated by using the Quartile function (the
+    inverted CDF) to give us the value on the CDF such that
+    BUILDTIME_PERCENT_CUTOFF (80%) of the mass of the distribution is
+    below the timeout value.
 
-    From http://en.wikipedia.org/wiki/Pareto_distribution#Definition,
-    the calculation we need is pow(BUILDTIME_PERCENT_CUTOFF/100.0, k)/Xm. 
+    Thus, we expect that the Tor client will accept the fastest 80% of
+    the total number of paths on the network.
 
+  Detecting Changing Network Conditions
+
+    We attempt to detect both network connectivity loss and drastic
+    changes in the timeout characteristics.
+
+    We assume that we've had network connectivity loss if 3 circuits
+    timeout and we've received no cells or TLS handshakes since those
+    circuits began. We then set the timeout to 60 seconds and stop
+    counting timeouts.
+
+    If 3 more circuits timeout and the network still has not been
+    live within this new 60 second timeout window, we then discard
+    the previous timeouts during this period from our history.
+
+    To detect changing network conditions, we keep a history of
+    the timeout or non-timeout status of the past RECENT_CIRCUITS (20)
+    that successfully completed at least one hop. If more than 75%
+    of these circuits timeout, we discard all buildtimes history,
+    reset the timeout to 60, and then begin recomputing the timeout.
+
   Testing
 
     After circuit build times, storage, and learning are implemented,
     the resulting histogram should be checked for consistency by
-    verifying it persists across successive Tor invocations where 
+    verifying it persists across successive Tor invocations where
     no circuits are built. In addition, we can also use the existing
-    buildtime scripts to record build times, and verify that the histogram 
+    buildtime scripts to record build times, and verify that the histogram
     the python produces matches that which is output to the state file in Tor,
     and verify that the Pareto parameters and cutoff points also match.
-  
-  Soft timeout vs Hard Timeout
-   
-    At some point, it may be desirable to change the cutoff from a 
-    single hard cutoff that destroys the circuit to a soft cutoff and
-    a hard cutoff, where the soft cutoff merely triggers the building
-    of a new circuit, and the hard cutoff triggers destruction of the 
-    circuit.
 
-    Good values for hard and soft cutoffs seem to be 85% and 65% 
-    respectively, but we should eventually justify this with observation.
+    We will also verify that there are no unexpected large deviations from
+    node selection, such as nodes from distant geographical locations being
+    completely excluded.
 
-  When to Begin Calculation
-
-    The number of circuits to observe (NCIRCUITS_TO_CUTOFF) before 
-    changing the CircuitBuildTimeout will be tunable via a #define. From 
-    our measurements, a good value for NCIRCUITS_TO_CUTOFF appears to be 
-    on the order of 100.
-
   Dealing with Timeouts
 
-    Timeouts should be counted as the expectation of the region of 
-    of the Pareto distribution beyond the cutoff. The proposal will
-    be updated with this value soon.
+    Timeouts should be counted as the expectation of the region of
+    of the Pareto distribution beyond the cutoff. This is done by
+    generating a random sample for each timeout at points on the
+    curve beyond the current timeout cutoff.
 
-    Also, in the event of network failure, the observation mechanism 
-    should stop collecting timeout data.
+  Future Work
 
-  Client Hints
+    At some point, it may be desirable to change the cutoff from a
+    single hard cutoff that destroys the circuit to a soft cutoff and
+    a hard cutoff, where the soft cutoff merely triggers the building
+    of a new circuit, and the hard cutoff triggers destruction of the
+    circuit.
 
-    Some research still needs to be done to provide initial values
-    for CircuitBuildTimeout based on values learned from modem
-    users, DSL users, Cable Modem users, and dedicated links. A 
-    radiobutton in Vidalia should eventually be provided that
-    sets CircuitBuildTimeout to one of these values and also 
-    provide the option of purging all learned data, should any exist.
+    It may also be beneficial to learn separate timeouts for each
+    guard node, as they will have slightly different distributions.
+    This will take longer to generate initial values though.
 
-    These values can either be published in the directory, or
-    shipped hardcoded for a particular Tor version.
-    
 Issues
 
   Impact on anonymity

Modified: tor/trunk/doc/spec/proposals/166-statistics-extra-info-docs.txt
===================================================================
--- tor/trunk/doc/spec/proposals/166-statistics-extra-info-docs.txt	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/doc/spec/proposals/166-statistics-extra-info-docs.txt	2009-09-25 17:06:41 UTC (rev 20669)
@@ -3,7 +3,7 @@
 Author: Karsten Loesing
 Created: 21-Jul-2009
 Target: 0.2.2
-Status: Open
+Status: Accepted
 
 Change history:
 
@@ -298,7 +298,7 @@
 
   The last type of statistics affects exit nodes counting the number of
   bytes written and read and the number of streams opened per port and
-  per 24 hours. Exit port statistics can be measured from looking of
+  per 24 hours. Exit port statistics can be measured from looking at
   headers of BEGIN and DATA cells. A BEGIN cell contains the exit port
   that is required for the exit node to open a new exit stream.
   Subsequent DATA cells coming from the client or being sent back to the
@@ -361,7 +361,7 @@
      basically means renaming keywords.
 
   2. The timing of writing the four *-stats files should be unified, so
-     that they are written exactly after 24 hours after starting the
+     that they are written exactly 24 hours after starting the
      relay. Right now, the measurement intervals for dirreq, entry, and
      exit stats starts with the first observed request, and files are
      written when observing the first request that occurs more than 24
@@ -373,14 +373,14 @@
      directory until they are included in extra-info documents. The
      reason is that the 24-hour measurement interval can be very
      different from the 18-hour publication interval of extra-info
-     documents. When a relay crashed after finishing a measurement
+     documents. When a relay crashes after finishing a measurement
      interval, but before publishing the next extra-info document,
      statistics would get lost. Therefore, statistics are written to
      disk when finishing a measurement interval and read from disk when
-     generating an extra-info document. As a result, the *-stats files
-     need to be overwritten after 24 hours, rather than appending new
-     statistics to them. Further, the contents of the *-stats files need
-     to be checked in the process of generating extra-info documents.
+     generating an extra-info document. Only the statistics that were
+     appended to the *-stats files within the past 24 hours are included
+     in extra-info documents. Further, the contents of the *-stats files
+     need to be checked in the process of generating extra-info documents.
 
   4. With the statistics patches being tested, the ./configure options
      should be removed and the statistics code be compiled by default.

Added: tor/trunk/doc/spec/proposals/167-params-in-consensus.txt
===================================================================
--- tor/trunk/doc/spec/proposals/167-params-in-consensus.txt	                        (rev 0)
+++ tor/trunk/doc/spec/proposals/167-params-in-consensus.txt	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,47 @@
+Filename: 167-params-in-consensus.txt
+Title: Vote on network parameters in consensus
+Author: Roger Dingledine
+Created: 18-Aug-2009
+Status: Closed
+Implemented-In: 0.2.2
+
+0. History
+
+
+1. Overview
+
+  Several of our new performance plans involve guessing how to tune
+  clients and relays, yet we won't be able to learn whether we guessed
+  the right tuning parameters until many people have upgraded. Instead,
+  we should have directory authorities vote on the parameters, and teach
+  Tors to read the currently recommended values out of the consensus.
+
+2. Design
+
+  V3 votes should include a new "params" line after the known-flags
+  line. It contains key=value pairs, where value is an integer.
+
+  Consensus documents that are generated with a sufficiently new consensus
+  method (7?) then include a params line that includes every key listed
+  in any vote, and the median value for that key (in case of ties,
+  we use the median closer to zero).
+
+2.1. Planned keys.
+
+  The first planned parameter is "circwindow=101", which is the initial
+  circuit packaging window that clients and relays should use. Putting
+  it in the consensus will let us perform experiments with different
+  values once enough Tors have upgraded -- see proposal 168.
+
+  Later parameters might include a weighting for how much to favor quiet
+  circuits over loud circuits in our round-robin algorithm; a weighting
+  for how much to prioritize relays over clients if we use an incentive
+  scheme like the gold-star design; and what fraction of circuits we
+  should throw out from proposal 151.
+
+2.2. What about non-integers?
+
+  I'm not sure how we would do median on non-integer values. Further,
+  I don't have any non-integer values in mind yet. So I say we cross
+  that bridge when we get to it.
+


Property changes on: tor/trunk/doc/spec/proposals/167-params-in-consensus.txt
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Added: tor/trunk/doc/spec/proposals/168-reduce-circwindow.txt
===================================================================
--- tor/trunk/doc/spec/proposals/168-reduce-circwindow.txt	                        (rev 0)
+++ tor/trunk/doc/spec/proposals/168-reduce-circwindow.txt	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,134 @@
+Filename: 168-reduce-circwindow.txt
+Title: Reduce default circuit window
+Author: Roger Dingledine
+Created: 12-Aug-2009
+Status: Open
+Target: 0.2.2
+
+0. History
+
+
+1. Overview
+
+  We should reduce the starting circuit "package window" from 1000 to
+  101. The lower package window will mean that clients will only be able
+  to receive 101 cells (~50KB) on a circuit before they need to send a
+  'sendme' acknowledgement cell to request 100 more.
+
+  Starting with a lower package window on exit relays should save on
+  buffer sizes (and thus memory requirements for the exit relay), and
+  should save on queue sizes (and thus latency for users).
+
+  Lowering the package window will induce an extra round-trip for every
+  additional 50298 bytes of the circuit. This extra step is clearly a
+  slow-down for large streams, but ultimately we hope that a) clients
+  fetching smaller streams will see better response, and b) slowing
+  down the large streams in this way will produce lower e2e latencies,
+  so the round-trips won't be so bad.
+
+2. Motivation
+
+  Karsten's torperf graphs show that the median download time for a 50KB
+  file over Tor in mid 2009 is 7.7 seconds, whereas the median download
+  time for 1MB and 5MB are around 50s and 150s respectively. The 7.7
+  second figure is way too high, whereas the 50s and 150s figures are
+  surprisingly low.
+
+  The median round-trip latency appears to be around 2s, with 25% of
+  the data points taking more than 5s. That's a lot of variance.
+
+  We designed Tor originally with the original goal of maximizing
+  throughput. We figured that would also optimize other network properties
+  like round-trip latency. Looks like we were wrong.
+
+3. Design
+
+  Wherever we initialize the circuit package window, initialize it to
+  101 rather than 1000. Reducing it should be safe even when interacting
+  with old Tors: the old Tors will receive the 101 cells and send back
+  a sendme ack cell. They'll still have much higher deliver windows,
+  but the rest of their deliver window will go unused.
+
+  You can find the patch at arma/circwindow. It seems to work.
+
+3.1. Why not 100?
+
+  Tor 0.0.0 through 0.2.1.19 have a bug where they only send the sendme
+  ack cell after 101 cells rather than the intended 100 cells.
+
+  Once 0.2.1.19 is obsolete we can change it back to 100 if we like. But
+  hopefully we'll have moved to some datagram protocol long before
+  0.2.1.19 becomes obsolete.
+
+3.2. What about stream packaging windows?
+
+  Right now the stream packaging windows start at 500. The goal was to
+  set the stream window to half the circuit window, to provide a crude
+  load balancing between streams on the same circuit. Once we lower
+  the circuit packaging window, the stream packaging window basically
+  becomes redundant.
+
+  We could leave it in -- it isn't hurting much in either case. Or we
+  could take it out -- people building other Tor clients would thank us
+  for that step. Alas, people building other Tor clients are going to
+  have to be compatible with current Tor clients, so in practice there's
+  no point taking out the stream packaging windows.
+
+3.3. What about variable circuit windows?
+
+  Once upon a time we imagined adapting the circuit package window to
+  the network conditions. That is, we would start the window small,
+  and raise it based on the latency and throughput we see.
+
+  In theory that crude imitation of TCP's windowing system would allow
+  us to adapt to fill the network better. In practice, I think we want
+  to stick with the small window and never raise it. The low cap reduces
+  the total throughput you can get from Tor for a given circuit. But
+  that's a feature, not a bug.
+
+4. Evaluation
+
+  How do we know this change is actually smart? It seems intuitive that
+  it's helpful, and some smart systems people have agreed that it's
+  a good idea (or said another way, they were shocked at how big the
+  default package window was before).
+
+  To get a more concrete sense of the benefit, though, Karsten has been
+  running torperf side-by-side on exit relays with the old package window
+  vs the new one. The results are mixed currently -- it is slightly faster
+  for fetching 40KB files, and slightly slower for fetching 50KB files.
+
+  I think it's going to be tough to get a clear conclusion that this is
+  a good design just by comparing one exit relay running the patch. The
+  trouble is that the other hops in the circuits are still getting bogged
+  down by other clients introducing too much traffic into the network.
+
+  Ultimately, we'll want to put the circwindow parameter into the
+  consensus so we can test a broader range of values once enough relays
+  have upgraded.
+
+5. Transition and deployment
+
+  We should put the circwindow in the consensus (see proposal 167),
+  with an initial value of 101. Then as more exit relays upgrade,
+  clients should seamlessly get the better behavior.
+
+  Note that upgrading the exit relay will only affect the "download"
+  package window. An old client that's uploading lots of bytes will
+  continue to use the old package window at the client side, and we
+  can't throttle that window at the exit side without breaking protocol.
+
+  The real question then is what we should backport to 0.2.1. Assuming
+  this could be a big performance win, we can't afford to wait until
+  0.2.2.x comes out before starting to see the changes here. So we have
+  two options as I see them:
+  a) once clients in 0.2.2.x know how to read the value out of the
+  consensus, and it's been tested for a bit, backport that part to
+  0.2.1.x.
+  b) if it's too complex to backport, just pick a number, like 101, and
+  backport that number.
+
+  Clearly choice (a) is the better one if the consensus parsing part
+  isn't very complex. Let's shoot for that, and fall back to (b) if the
+  patch turns out to be so big that we reconsider.
+


Property changes on: tor/trunk/doc/spec/proposals/168-reduce-circwindow.txt
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Modified: tor/trunk/doc/tor-osx-dmg-creation.txt
===================================================================
--- tor/trunk/doc/tor-osx-dmg-creation.txt	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/doc/tor-osx-dmg-creation.txt	2009-09-25 17:06:41 UTC (rev 20669)
@@ -1,31 +1,62 @@
 ## Instructions for building the official dmgs for OSX.
 ##
+## The loose table of contents:
+## Summary
+## Single Architecture Binaries for PPC or X86, not both.
+## Backwards compatible single-architecture binaries for OSX x86 10.4 from newer versions of OS X.
+## Universal Binaries for OSX PPC and X86
+## Each section is delineated by ###.
 
 The following steps are the exact steps used to produce the "official"
 OSX builds of tor.
 
-Summary:
+### Summary:
 1) Compile and install a static version of the latest release of
 libevent.
 2) Acquire and install your preferred version of tor. Extract.
 3) "make dist-osx"
 4) You now have a dmg from which you can install Tor.
 
-## Universal Binaries for OSX PPC and X86
-## This method works in OSX 10.4 (Tiger) and newer OSX versions.
-## See far below if you don't care about cross compiling for PPC and X86.
-## The single architecture process starts with "###"
+### Single Architecture Binaries for PPC or X86, not both.
+### This method works in all versions of OSX 10.3 through 10.6
 
+## Compiling libevent ##
+
+1)  Download the latest stable libevent from
+http://www.monkey.org/~provos/libevent/
+
+2) The first step of compiling libevent is to configure it as
+follows:
+       ./configure --enable-static --disable-shared
+
+3) Complete the "make" and "make install".  You will need to be root,
+or sudo -s, to complete the "make install".
+
+## Compiling Tor ##
+
+4) Get your preferred version of the tor source from https://www.torproject.org.  Extract the
+tarball.
+
+5) In the top level, this means /path/to/tor/, not tor/contrib/osx,
+do a configure with these parameters:
+     CONFDIR=/Library/Tor ./configure --prefix=/Library/Tor \
+     --bindir=/Library/Tor --sysconfdir=/Library
+
+6) In same top level dir, do a "make dist-osx".  There now exists a
+.dmg file in the same directory.  Install from this dmg.
+
+### Backwards compatible single-architecture binaries for OSX x86 10.4 from newer versions of OS X.
+
 1) Install the latest XCode updates available from http://developer.apple.com.
 
-## Compiling libevent
+## Compiling libevent ##
 
 2)  Download latest stable libevent from
 http://www.monkey.org/~provos/libevent/
 
 3) The first step of compiling libevent is to configure it as
 follows:
-CFLAGS="-O -g -mmacosx-version-min=10.4 -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc" \
+CFLAGS="-O -g -mmacosx-version-min=10.4 -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386" \
 LDFLAGS="-Wl,-syslibroot,/Developer/SDKs/MacOSX10.4u.sdk" \
 ./configure --enable-static --disable-shared --disable-dependency-tracking
 
@@ -37,16 +68,14 @@
 	"file /usr/local/lib/libevent.a"
 
 	Your output should be:
-/usr/local/lib/libevent.a: Mach-O fat file with 2 architectures
 /usr/local/lib/libevent.a (for architecture i386):      current ar archive random library
-/usr/local/lib/libevent.a (for architecture ppc):       current ar archive
 
 6) Get your preferred version of the tor source from https://www.torproject.org/download.  
 Extract the tarball.
 
 7) In the top level, this means /path/to/tor/, not tor/contrib/osx,
 do a configure with these parameters:
-CFLAGS="-O -g -mmacosx-version-min=10.4 -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc" \
+CFLAGS="-O -g -mmacosx-version-min=10.4 -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386" \
 LDFLAGS="-Wl,-syslibroot,/Developer/SDKs/MacOSX10.4u.sdk" \
 CONFDIR=/Library/Tor \
 ./configure --prefix=/Library/Tor --bindir=/Library/Tor \
@@ -56,39 +85,61 @@
 
 9) Confirm you have created a universal binary by issuing the follow command:
 "file src/or/tor".  Its output should be as follows:
-src/or/tor: Mach-O fat file with 2 architectures
 src/or/tor (for architecture i386):     Mach-O executable i386
-src/or/tor (for architecture ppc):      Mach-O executable ppc
 
 10) There should exist in the top-level directory a
 Tor-$VERSION-universal-Bundle.dmg
 
-11) Congrats.  You have a universal binary. You are now ready to install Tor.
+11) Congrats.  You have a backwards-compatible binary. You are now ready to install Tor.
 
-### Single Architecture Binaries for PPC or X86, not both.
-### This method works in all versions of OSX 10.3 through 10.5
+### Universal Binaries for OSX PPC and X86
+### This method works in OSX 10.4 (Tiger) and newer OSX versions.
 
-### Compiling libevent
+1) Install the latest XCode updates available from http://developer.apple.com.
 
-1)  Download the latest stable libevent from
+## Compiling libevent ##
+
+2)  Download latest stable libevent from
 http://www.monkey.org/~provos/libevent/
 
-2) The first step of compiling libevent is to configure it as
+3) The first step of compiling libevent is to configure it as
 follows:
-       ./configure --enable-static --disable-shared
+CFLAGS="-O -g -mmacosx-version-min=10.4 -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc" \
+LDFLAGS="-Wl,-syslibroot,/Developer/SDKs/MacOSX10.4u.sdk" \
+./configure --enable-static --disable-shared --disable-dependency-tracking
 
-3) Complete the "make" and "make install".  You will need to be root,
+4) Complete the "make" and "make install".  You will need to be root,
 or sudo -s, to complete the "make install".
 
-### Compiling Tor
+5) Check for a successful universal binary of libevent.a in, by default,
+/usr/local/lib by using the following command:
+	"file /usr/local/lib/libevent.a"
 
-4) Get your preferred version of the tor source from https://www.torproject.org.  Extract the
-tarball.
+	Your output should be:
+/usr/local/lib/libevent.a: Mach-O fat file with 2 architectures
+/usr/local/lib/libevent.a (for architecture i386):      current ar archive random library
+/usr/local/lib/libevent.a (for architecture ppc):       current ar archive
 
-5) In the top level, this means /path/to/tor/, not tor/contrib/osx,
+6) Get your preferred version of the tor source from https://www.torproject.org/download.  
+Extract the tarball.
+
+7) In the top level, this means /path/to/tor/, not tor/contrib/osx,
 do a configure with these parameters:
-     CONFDIR=/Library/Tor ./configure --prefix=/Library/Tor \
-     --bindir=/Library/Tor --sysconfdir=/Library
+CFLAGS="-O -g -mmacosx-version-min=10.4 -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc" \
+LDFLAGS="-Wl,-syslibroot,/Developer/SDKs/MacOSX10.4u.sdk" \
+CONFDIR=/Library/Tor \
+./configure --prefix=/Library/Tor --bindir=/Library/Tor \
+--sysconfdir=/Library --disable-dependency-tracking
 
-6) In same top level dir, do a "make dist-osx".  There now exists a
-.dmg file in the same directory.  Install from this dmg.
+8) "make dist-osx"
+
+9) Confirm you have created a universal binary by issuing the follow command:
+"file src/or/tor".  Its output should be as follows:
+src/or/tor: Mach-O fat file with 2 architectures
+src/or/tor (for architecture i386):     Mach-O executable i386
+src/or/tor (for architecture ppc):      Mach-O executable ppc
+
+10) There should exist in the top-level directory a
+Tor-$VERSION-universal-Bundle.dmg
+
+11) Congrats.  You have a universal binary. You are now ready to install Tor.

Modified: tor/trunk/doc/tor.1.in
===================================================================
--- tor/trunk/doc/tor.1.in	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/doc/tor.1.in	2009-09-25 17:06:41 UTC (rev 20669)
@@ -1,4 +1,4 @@
-.TH TOR 1 "January 2009" "TOR"
+.TH TOR 1 "August 2009" "TOR"
 .SH NAME
 tor \- The second-generation onion router
 .SH SYNOPSIS
@@ -299,6 +299,25 @@
 patch if you want it to support others.
 .LP
 .TP
+\fBSocks4Proxy\fR \fIhost\fR[:\fIport\fR]\fP
+Tor will make all OR connections through the SOCKS 4 proxy at host:port
+(or host:1080 if port is not specified).
+.LP
+.TP
+\fBSocks5Proxy\fR \fIhost\fR[:\fIport\fR]\fP
+Tor will make all OR connections through the SOCKS 5 proxy at host:port
+(or host:1080 if port is not specified).
+.LP
+.TP
+\fBSocks5ProxyUsername\fR \fIusername\fP
+.LP
+.TP
+\fBSocks5ProxyPassword\fR \fIpassword\fP
+If defined, authenticate to the SOCKS 5 server using username and password
+in accordance to RFC 1929. Both username and password must be between 1 and 255
+characters.
+.LP
+.TP
 \fBKeepalivePeriod \fR\fINUM\fP
 To keep firewalls from expiring connections, send a padding keepalive
 cell every NUM seconds on open connections that are in use. If the
@@ -1056,6 +1075,36 @@
 .TP
 \fBGeoIPFile \fR\fIfilename\fP
 A filename containing GeoIP data, for use with BridgeRecordUsageByCountry.
+.LP
+.TP
+\fBCellStatistics \fR\fB0\fR|\fB1\fR\fP
+When this option is enabled, Tor writes statistics on the mean time that
+cells spend in circuit queues to disk every 24 hours. Cannot be changed
+while Tor is running. (Default: 0)
+.LP
+.TP
+\fBDirReqStatistics \fR\fB0\fR|\fB1\fR\fP
+When this option is enabled, Tor writes statistics on the number and
+response time of network status requests to disk every 24 hours. Cannot be
+changed while Tor is running. (Default: 0)
+.LP
+.TP
+\fBEntryStatistics \fR\fB0\fR|\fB1\fR\fP
+When this option is enabled, Tor writes statistics on the number of
+directly connecting clients to disk every 24 hours. Cannot be changed
+while Tor is running. (Default: 0)
+.LP
+.TP
+\fBExitPortStatistics \fR\fB0\fR|\fB1\fR\fP
+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.
+Cannot be changed while Tor is running. (Default: 0)
+.LP
+.TP
+\fBExtraInfoStatistics \fR\fB0\fR|\fB1\fR\fP
+When this option is enabled, Tor includes previously gathered statistics
+in its extra-info documents that it uploads to the directory authorities.
+(Default: 0)
 
 .SH DIRECTORY SERVER OPTIONS
 .PP
@@ -1194,6 +1243,11 @@
 \fBVersioningAuthoritativeDirectory\fP should be set too.
 .LP
 .TP
+\fBConsensusParams \fR\fISTRING\fP
+STRING is a space-separated list of key=value pairs that Tor will
+include in the "params" line of its networkstatus vote.
+.LP
+.TP
 \fBDirAllowPrivateAddresses \fR\fB0\fR|\fB1\fR\fP
 If set to 1, Tor will accept router descriptors with arbitrary "Address"
 elements. Otherwise, if the address is not an IP address or is a private
@@ -1320,7 +1374,7 @@
 .TP
 \fBHiddenServiceVersion \fR\fIversion\fR,\fIversion\fR,\fI...\fP
 A list of rendezvous service descriptor versions to publish for the hidden
-service. Possible version numbers are 0 and 2. (Default: 0, 2)
+service. Currently, only version 2 is supported. (Default: 2)
 .LP
 .TP
 \fBHiddenServiceAuthorizeClient \fR\fIauth-type\fR \fR\fIclient-name\fR,\fIclient-name\fR,\fI...\fP
@@ -1465,7 +1519,7 @@
 .LP
 .TP
 .B \fIDataDirectory\fB/cached-descriptors\fR and \fBcached-descriptors.new\fR
-These files hold downloaded router statuses.  Some routers may appear more than once; if so, the most recently published descriptor is used.    Lines beginning with @-signs are annotations that contain more information about a given router.  The ".new" file is an append-only journal; when it gets too large, all entries are merged into a new cached-routers file.
+These files hold downloaded router statuses.  Some routers may appear more than once; if so, the most recently published descriptor is used.    Lines beginning with @-signs are annotations that contain more information about a given router.  The ".new" file is an append-only journal; when it gets too large, all entries are merged into a new cached-descriptors file.
 .LP
 .TP
 .B \fIDataDirectory\fB/cached-routers\fR and \fBcached-routers.new\fR

Modified: tor/trunk/src/Makefile.am
===================================================================
--- tor/trunk/src/Makefile.am	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/Makefile.am	2009-09-25 17:06:41 UTC (rev 20669)
@@ -1,5 +1,5 @@
 
 # leave in dependency order, since common must be built first
-SUBDIRS =      common or tools win32 config
-DIST_SUBDIRS = common or tools win32 config
+SUBDIRS =      common or test tools win32 config
+DIST_SUBDIRS = common or test tools win32 config
 

Modified: tor/trunk/src/common/Makefile.am
===================================================================
--- tor/trunk/src/common/Makefile.am	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/common/Makefile.am	2009-09-25 17:06:41 UTC (rev 20669)
@@ -1,7 +1,7 @@
 
 noinst_LIBRARIES = libor.a libor-crypto.a libor-event.a
 
-EXTRA_DIST = common_sha1.i
+EXTRA_DIST = common_sha1.i sha256.c
 
 #CFLAGS  = -Wall -Wpointer-arith -O2
 
@@ -16,7 +16,7 @@
 libor_crypto_a_SOURCES = crypto.c aes.c tortls.c torgzip.c
 libor_event_a_SOURCES = compat_libevent.c
 
-noinst_HEADERS = address.h log.h crypto.h test.h util.h compat.h aes.h torint.h tortls.h strlcpy.c strlcat.c torgzip.h container.h ht.h mempool.h memarea.h ciphers.inc compat_libevent.h
+noinst_HEADERS = address.h log.h crypto.h util.h compat.h aes.h torint.h tortls.h strlcpy.c strlcat.c torgzip.h container.h ht.h mempool.h memarea.h ciphers.inc compat_libevent.h tortls_states.h
 
 common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)
 	if test "@SHA1SUM@" != none; then \
@@ -29,3 +29,4 @@
 	fi
 
 util_codedigest.o: common_sha1.i
+crypto.o: sha256.c

Modified: tor/trunk/src/common/address.c
===================================================================
--- tor/trunk/src/common/address.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/common/address.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -373,10 +373,11 @@
       return -1; /* malformed. */
 
     /* reverse the bytes */
-    inaddr.s_addr = (((inaddr.s_addr & 0x000000fful) << 24)
-                     |((inaddr.s_addr & 0x0000ff00ul) << 8)
-                     |((inaddr.s_addr & 0x00ff0000ul) >> 8)
-                     |((inaddr.s_addr & 0xff000000ul) >> 24));
+    inaddr.s_addr = (uint32_t)
+      (((inaddr.s_addr & 0x000000ff) << 24)
+       |((inaddr.s_addr & 0x0000ff00) << 8)
+       |((inaddr.s_addr & 0x00ff0000) >> 8)
+       |((inaddr.s_addr & 0xff000000) >> 24));
 
     if (result) {
       tor_addr_from_in(result, &inaddr);

Modified: tor/trunk/src/common/compat.c
===================================================================
--- tor/trunk/src/common/compat.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/common/compat.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -480,8 +480,8 @@
   return v;
 }
 /**
- * Read a 32-bit value beginning at <b>cp</b>.  Equivalent to
- * *(uint32_t*)(cp), but will not cause segfaults on platforms that forbid
+ * Read a 64-bit value beginning at <b>cp</b>.  Equivalent to
+ * *(uint64_t*)(cp), but will not cause segfaults on platforms that forbid
  * unaligned memory access.
  */
 uint64_t

Modified: tor/trunk/src/common/container.c
===================================================================
--- tor/trunk/src/common/container.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/common/container.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -1220,6 +1220,7 @@
 IMPLEMENT_ORDER_FUNC(find_nth_time, time_t)
 IMPLEMENT_ORDER_FUNC(find_nth_double, double)
 IMPLEMENT_ORDER_FUNC(find_nth_uint32, uint32_t)
+IMPLEMENT_ORDER_FUNC(find_nth_int32, int32_t)
 IMPLEMENT_ORDER_FUNC(find_nth_long, long)
 
 /** Return a newly allocated digestset_t, optimized to hold a total of

Modified: tor/trunk/src/common/container.h
===================================================================
--- tor/trunk/src/common/container.h	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/common/container.h	2009-09-25 17:06:41 UTC (rev 20669)
@@ -627,6 +627,7 @@
 int find_nth_int(int *array, int n_elements, int nth);
 time_t find_nth_time(time_t *array, int n_elements, int nth);
 double find_nth_double(double *array, int n_elements, int nth);
+int32_t find_nth_int32(int32_t *array, int n_elements, int nth);
 uint32_t find_nth_uint32(uint32_t *array, int n_elements, int nth);
 long find_nth_long(long *array, int n_elements, int nth);
 static INLINE int
@@ -649,6 +650,11 @@
 {
   return find_nth_uint32(array, n_elements, (n_elements-1)/2);
 }
+static INLINE int32_t
+median_int32(int32_t *array, int n_elements)
+{
+  return find_nth_int32(array, n_elements, (n_elements-1)/2);
+}
 static INLINE long
 median_long(long *array, int n_elements)
 {

Modified: tor/trunk/src/common/crypto.c
===================================================================
--- tor/trunk/src/common/crypto.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/common/crypto.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -62,6 +62,28 @@
 
 #include <openssl/engine.h>
 
+#if OPENSSL_VERSION_NUMBER < 0x00908000l
+/* On OpenSSL versions before 0.9.8, there is no working SHA256
+ * implementation, so we use Tom St Denis's nice speedy one, slightly adapted
+ * to our needs */
+#define SHA256_CTX sha256_state
+#define SHA256_Init sha256_init
+#define SHA256_Update sha256_process
+#define LTC_ARGCHK(x) tor_assert(x)
+#include "sha256.c"
+#define SHA256_Final(a,b) sha256_done(b,a)
+
+static unsigned char *
+SHA256(const unsigned char *m, size_t len, unsigned char *d)
+{
+  SHA256_CTX ctx;
+  SHA256_Init(&ctx);
+  SHA256_Update(&ctx, m, len);
+  SHA256_Final(d, &ctx);
+  return d;
+}
+#endif
+
 /** Macro: is k a valid RSA public or private key? */
 #define PUBLIC_KEY_OK(k) ((k) && (k)->key && (k)->key->n)
 /** Macro: is k a valid RSA private key? */
@@ -1394,9 +1416,23 @@
   return (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL);
 }
 
+int
+crypto_digest256(char *digest, const char *m, size_t len,
+                 digest_algorithm_t algorithm)
+{
+  tor_assert(m);
+  tor_assert(digest);
+  tor_assert(algorithm == DIGEST_SHA256);
+  return (SHA256((const unsigned char*)m,len,(unsigned char*)digest) == NULL);
+}
+
 /** Intermediate information about the digest of a stream of data. */
 struct crypto_digest_env_t {
-  SHA_CTX d;
+  union {
+    SHA_CTX sha1;
+    SHA256_CTX sha2;
+  } d;
+  digest_algorithm_t algorithm : 8;
 };
 
 /** Allocate and return a new digest object.
@@ -1406,10 +1442,22 @@
 {
   crypto_digest_env_t *r;
   r = tor_malloc(sizeof(crypto_digest_env_t));
-  SHA1_Init(&r->d);
+  SHA1_Init(&r->d.sha1);
+  r->algorithm = DIGEST_SHA1;
   return r;
 }
 
+crypto_digest_env_t *
+crypto_new_digest256_env(digest_algorithm_t algorithm)
+{
+  crypto_digest_env_t *r;
+  tor_assert(algorithm == DIGEST_SHA256);
+  r = tor_malloc(sizeof(crypto_digest_env_t));
+  SHA256_Init(&r->d.sha2);
+  r->algorithm = algorithm;
+  return r;
+}
+
 /** Deallocate a digest object.
  */
 void
@@ -1427,30 +1475,51 @@
 {
   tor_assert(digest);
   tor_assert(data);
-  /* Using the SHA1_*() calls directly means we don't support doing
-   * SHA1 in hardware. But so far the delay of getting the question
+  /* Using the SHA*_*() calls directly means we don't support doing
+   * SHA in hardware. But so far the delay of getting the question
    * to the hardware, and hearing the answer, is likely higher than
    * just doing it ourselves. Hashes are fast.
    */
-  SHA1_Update(&digest->d, (void*)data, len);
+  switch (digest->algorithm) {
+    case DIGEST_SHA1:
+      SHA1_Update(&digest->d.sha1, (void*)data, len);
+      break;
+    case DIGEST_SHA256:
+      SHA256_Update(&digest->d.sha2, (void*)data, len);
+      break;
+    default:
+      tor_fragile_assert();
+      break;
+  }
 }
 
 /** Compute the hash of the data that has been passed to the digest
  * object; write the first out_len bytes of the result to <b>out</b>.
- * <b>out_len</b> must be \<= DIGEST_LEN.
+ * <b>out_len</b> must be \<= DIGEST256_LEN.
  */
 void
 crypto_digest_get_digest(crypto_digest_env_t *digest,
                          char *out, size_t out_len)
 {
-  unsigned char r[DIGEST_LEN];
-  SHA_CTX tmpctx;
+  unsigned char r[DIGEST256_LEN];
+  crypto_digest_env_t tmpenv;
   tor_assert(digest);
   tor_assert(out);
-  tor_assert(out_len <= DIGEST_LEN);
-  /* memcpy into a temporary ctx, since SHA1_Final clears the context */
-  memcpy(&tmpctx, &digest->d, sizeof(SHA_CTX));
-  SHA1_Final(r, &tmpctx);
+  /* memcpy into a temporary ctx, since SHA*_Final clears the context */
+  memcpy(&tmpenv, digest, sizeof(crypto_digest_env_t));
+  switch (digest->algorithm) {
+    case DIGEST_SHA1:
+      tor_assert(out_len <= DIGEST_LEN);
+      SHA1_Final(r, &tmpenv.d.sha1);
+      break;
+    case DIGEST_SHA256:
+      tor_assert(out_len <= DIGEST256_LEN);
+      SHA256_Final(r, &tmpenv.d.sha2);
+      break;
+    default:
+      tor_fragile_assert();
+      break;
+  }
   memcpy(out, r, out_len);
   memset(r, 0, sizeof(r));
 }

Modified: tor/trunk/src/common/crypto.h
===================================================================
--- tor/trunk/src/common/crypto.h	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/common/crypto.h	2009-09-25 17:06:41 UTC (rev 20669)
@@ -18,6 +18,9 @@
 
 /** Length of the output of our message digest. */
 #define DIGEST_LEN 20
+/** Length of the output of our second (improved) message digests.  (For now
+ * this is just sha256, but any it can be any other 256-byte digest). */
+#define DIGEST256_LEN 32
 /** Length of our symmetric cipher's keys. */
 #define CIPHER_KEY_LEN 16
 /** Length of our symmetric cipher's IV. */
@@ -27,9 +30,12 @@
 /** Length of our DH keys. */
 #define DH_BYTES (1024/8)
 
-/** Length of a message digest when encoded in base64 with trailing = signs
- * removed. */
+/** Length of a sha1 message digest when encoded in base64 with trailing =
+ * signs removed. */
 #define BASE64_DIGEST_LEN 27
+/** Length of a sha256 message digest when encoded in base64 with trailing =
+ * signs removed. */
+#define BASE64_DIGEST256_LEN 43
 
 /** Constants used to indicate no padding for public-key encryption */
 #define PK_NO_PADDING         60000
@@ -48,7 +54,14 @@
 #define FINGERPRINT_LEN 49
 /** Length of hex encoding of SHA1 digest, not including final NUL. */
 #define HEX_DIGEST_LEN 40
+/** Length of hex encoding of SHA256 digest, not including final NUL. */
+#define HEX_DIGEST256_LEN 64
 
+typedef enum {
+  DIGEST_SHA1,
+  DIGEST_SHA256,
+} digest_algorithm_t;
+
 typedef struct crypto_pk_env_t crypto_pk_env_t;
 typedef struct crypto_cipher_env_t crypto_cipher_env_t;
 typedef struct crypto_digest_env_t crypto_digest_env_t;
@@ -145,7 +158,10 @@
 
 /* SHA-1 */
 int crypto_digest(char *digest, const char *m, size_t len);
+int crypto_digest256(char *digest, const char *m, size_t len,
+                     digest_algorithm_t algorithm);
 crypto_digest_env_t *crypto_new_digest_env(void);
+crypto_digest_env_t *crypto_new_digest256_env(digest_algorithm_t algorithm);
 void crypto_free_digest_env(crypto_digest_env_t *digest);
 void crypto_digest_add_bytes(crypto_digest_env_t *digest, const char *data,
                              size_t len);

Modified: tor/trunk/src/common/log.c
===================================================================
--- tor/trunk/src/common/log.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/common/log.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -92,7 +92,8 @@
 }
 
 /** A mutex to guard changes to logfiles and logging. */
-static tor_mutex_t *log_mutex = NULL;
+static tor_mutex_t log_mutex;
+static int log_mutex_initialized = 0;
 
 /** Linked list of logfile_t. */
 static logfile_t *logfiles = NULL;
@@ -103,9 +104,9 @@
 #endif
 
 #define LOCK_LOGS() STMT_BEGIN                                          \
-  tor_mutex_acquire(log_mutex);                                         \
+  tor_mutex_acquire(&log_mutex);                                        \
   STMT_END
-#define UNLOCK_LOGS() STMT_BEGIN tor_mutex_release(log_mutex); STMT_END
+#define UNLOCK_LOGS() STMT_BEGIN tor_mutex_release(&log_mutex); STMT_END
 
 /** What's the lowest log level anybody cares about?  Checking this lets us
  * bail out early from log_debug if we aren't debugging.  */
@@ -146,8 +147,8 @@
   t = (time_t)now.tv_sec;
 
   n = strftime(buf, buf_len, "%b %d %H:%M:%S", tor_localtime_r(&t, &tm));
-  r = tor_snprintf(buf+n, buf_len-n, ".%.3ld [%s] ",
-                   (long)now.tv_usec / 1000, sev_to_string(severity));
+  r = tor_snprintf(buf+n, buf_len-n, ".%.3i [%s] ",
+                   (int)now.tv_usec / 1000, sev_to_string(severity));
   if (r<0)
     return buf_len-1;
   else
@@ -446,8 +447,9 @@
     log_free(victim);
   }
   tor_free(appname);
-  tor_mutex_free(log_mutex);
-  log_mutex = NULL;
+
+  /* We _could_ destroy the log mutex here, but that would screw up any logs
+   * that happened between here and the end of execution. */
 }
 
 /** Remove and free the log entry <b>victim</b> from the linked-list
@@ -543,8 +545,10 @@
 void
 init_logging(void)
 {
-  if (!log_mutex)
-    log_mutex = tor_mutex_new();
+  if (!log_mutex_initialized) {
+    tor_mutex_init(&log_mutex);
+    log_mutex_initialized = 1;
+  }
 }
 
 /** Add a log handler to receive messages during startup (before the real
@@ -741,7 +745,7 @@
 static const char *domain_list[] = {
   "GENERAL", "CRYPTO", "NET", "CONFIG", "FS", "PROTOCOL", "MM",
   "HTTP", "APP", "CONTROL", "CIRC", "REND", "BUG", "DIR", "DIRSERV",
-  "OR", "EDGE", "ACCT", "HIST", NULL
+  "OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", NULL
 };
 
 /** Return a bitmask for the log domain for which <b>domain</b> is the name,

Modified: tor/trunk/src/common/log.h
===================================================================
--- tor/trunk/src/common/log.h	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/common/log.h	2009-09-25 17:06:41 UTC (rev 20669)
@@ -90,9 +90,10 @@
 #define LD_ACCT     (1u<<17)
 /** Router history */
 #define LD_HIST     (1u<<18)
-
+/** OR handshaking */
+#define LD_HANDSHAKE (1u<<19)
 /** Number of logging domains in the code. */
-#define N_LOGGING_DOMAINS 19
+#define N_LOGGING_DOMAINS 20
 
 typedef uint32_t log_domain_mask_t;
 

Added: tor/trunk/src/common/sha256.c
===================================================================
--- tor/trunk/src/common/sha256.c	                        (rev 0)
+++ tor/trunk/src/common/sha256.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,331 @@
+/* Copyright (c) 2009, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+/* This SHA256 implementation is adapted from the public domain one in
+   LibTomCrypt, version 1.6.  Tor uses it on platforms where OpenSSL doesn't
+   have a SHA256. */
+
+
+typedef struct sha256_state {
+    uint64_t length;
+    uint32_t state[8], curlen;
+    unsigned char buf[64];
+} sha256_state;
+
+#define CRYPT_OK 0
+#define CRYPT_NOP -1
+#define CRYPT_INVALID_ARG -2
+
+#define LOAD32H(x,y) STMT_BEGIN x = ntohl(get_uint32((const char*)y)); STMT_END
+#define STORE32H(x,y) STMT_BEGIN set_uint32((char*)y, htonl(x)); STMT_END
+#define STORE64H(x,y) STMT_BEGIN                                \
+  set_uint32((char*)y, htonl((uint32_t)((x)>>32)));             \
+  set_uint32(((char*)y)+4, htonl((uint32_t)((x)&0xffffffff)));  \
+  STMT_END
+#define RORc(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
+#ifndef MIN
+   #define MIN(x, y) ( ((x)<(y))?(x):(y) )
+#endif
+
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis at gmail.com, http://libtomcrypt.com
+ */
+
+/**
+  @file sha256.c
+  SHA256 by Tom St Denis
+*/
+
+
+#ifdef LTC_SMALL_CODE
+/* the K array */
+static const uint32_t K[64] = {
+    0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+    0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+    0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+    0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+    0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+    0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+    0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+    0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+    0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+    0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+    0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+    0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+    0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+#endif
+
+/* Various logical functions */
+#define Ch(x,y,z)       (z ^ (x & (y ^ z)))
+#define Maj(x,y,z)      (((x | y) & z) | (x & y))
+#define S(x, n)         RORc((x),(n))
+#define R(x, n)         (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0(x)       (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1(x)       (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0(x)       (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1(x)       (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+
+/* compress 512-bits */
+#ifdef LTC_CLEAN_STACK
+static int _sha256_compress(sha256_state * md, unsigned char *buf)
+#else
+static int  sha256_compress(sha256_state * md, unsigned char *buf)
+#endif
+{
+    uint32_t S[8], W[64], t0, t1;
+#ifdef LTC_SMALL_CODE
+    uint32_t t;
+#endif
+    int i;
+
+    /* copy state into S */
+    for (i = 0; i < 8; i++) {
+        S[i] = md->state[i];
+    }
+
+    /* copy the state into 512-bits into W[0..15] */
+    for (i = 0; i < 16; i++) {
+        LOAD32H(W[i], buf + (4*i));
+    }
+
+    /* fill W[16..63] */
+    for (i = 16; i < 64; i++) {
+        W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
+    }
+
+    /* Compress */
+#ifdef LTC_SMALL_CODE
+#define RND(a,b,c,d,e,f,g,h,i)                         \
+     t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];   \
+     t1 = Sigma0(a) + Maj(a, b, c);                    \
+     d += t0;                                          \
+     h  = t0 + t1;
+
+     for (i = 0; i < 64; ++i) {
+         RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i);
+         t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
+         S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
+     }
+#else
+#define RND(a,b,c,d,e,f,g,h,i,ki)                    \
+     t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i];   \
+     t1 = Sigma0(a) + Maj(a, b, c);                  \
+     d += t0;                                        \
+     h  = t0 + t1;
+
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3);
+    RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee);
+    RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f);
+    RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814);
+    RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208);
+    RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa);
+    RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb);
+    RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7);
+    RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2);
+
+#undef RND
+
+#endif
+
+    /* feedback */
+    for (i = 0; i < 8; i++) {
+        md->state[i] = md->state[i] + S[i];
+    }
+    return CRYPT_OK;
+}
+
+#ifdef LTC_CLEAN_STACK
+static int sha256_compress(sha256_state * md, unsigned char *buf)
+{
+    int err;
+    err = _sha256_compress(md, buf);
+    burn_stack(sizeof(uint32_t) * 74);
+    return err;
+}
+#endif
+
+/**
+   Initialize the hash state
+   @param md   The hash state you wish to initialize
+   @return CRYPT_OK if successful
+*/
+static int sha256_init(sha256_state * md)
+{
+    LTC_ARGCHK(md != NULL);
+
+    md->curlen = 0;
+    md->length = 0;
+    md->state[0] = 0x6A09E667UL;
+    md->state[1] = 0xBB67AE85UL;
+    md->state[2] = 0x3C6EF372UL;
+    md->state[3] = 0xA54FF53AUL;
+    md->state[4] = 0x510E527FUL;
+    md->state[5] = 0x9B05688CUL;
+    md->state[6] = 0x1F83D9ABUL;
+    md->state[7] = 0x5BE0CD19UL;
+    return CRYPT_OK;
+}
+
+/**
+   Process a block of memory though the hash
+   @param md     The hash state
+   @param in     The data to hash
+   @param inlen  The length of the data (octets)
+   @return CRYPT_OK if successful
+*/
+static int sha256_process (sha256_state * md, const unsigned char *in, unsigned long inlen)
+{
+    unsigned long n;
+    int           err;
+    LTC_ARGCHK(md != NULL);
+    LTC_ARGCHK(in != NULL);
+    if (md->curlen > sizeof(md->buf)) {
+       return CRYPT_INVALID_ARG;
+    }
+    while (inlen > 0) {
+        if (md->curlen == 0 && inlen >= 64) {
+           if ((err = sha256_compress (md, (unsigned char *)in)) != CRYPT_OK) {
+              return err;
+           }
+           md->length += 64 * 8;
+           in             += 64;
+           inlen          -= 64;
+        } else {
+           n = MIN(inlen, (64 - md->curlen));
+           memcpy(md->buf + md->curlen, in, (size_t)n);
+           md->curlen += n;
+           in             += n;
+           inlen          -= n;
+           if (md->curlen == 64) {
+              if ((err = sha256_compress (md, md->buf)) != CRYPT_OK) {
+                 return err;
+              }
+              md->length += 8*64;
+              md->curlen = 0;
+           }
+       }
+    }
+    return CRYPT_OK;
+}
+
+/**
+   Terminate the hash to get the digest
+   @param md  The hash state
+   @param out [out] The destination of the hash (32 bytes)
+   @return CRYPT_OK if successful
+*/
+static int sha256_done(sha256_state * md, unsigned char *out)
+{
+    int i;
+
+    LTC_ARGCHK(md  != NULL);
+    LTC_ARGCHK(out != NULL);
+
+    if (md->curlen >= sizeof(md->buf)) {
+       return CRYPT_INVALID_ARG;
+    }
+
+
+    /* increase the length of the message */
+    md->length += md->curlen * 8;
+
+    /* append the '1' bit */
+    md->buf[md->curlen++] = (unsigned char)0x80;
+
+    /* if the length is currently above 56 bytes we append zeros
+     * then compress.  Then we can fall back to padding zeros and length
+     * encoding like normal.
+     */
+    if (md->curlen > 56) {
+        while (md->curlen < 64) {
+            md->buf[md->curlen++] = (unsigned char)0;
+        }
+        sha256_compress(md, md->buf);
+        md->curlen = 0;
+    }
+
+    /* pad upto 56 bytes of zeroes */
+    while (md->curlen < 56) {
+        md->buf[md->curlen++] = (unsigned char)0;
+    }
+
+    /* store length */
+    STORE64H(md->length, md->buf+56);
+    sha256_compress(md, md->buf);
+
+    /* copy output */
+    for (i = 0; i < 8; i++) {
+        STORE32H(md->state[i], out+(4*i));
+    }
+#ifdef LTC_CLEAN_STACK
+    zeromem(md, sizeof(sha256_state));
+#endif
+    return CRYPT_OK;
+}
+
+/* $Source: /cvs/libtom/libtomcrypt/src/hashes/sha2/sha256.c,v $ */
+/* $Revision$ */
+/* $Date$ */


Property changes on: tor/trunk/src/common/sha256.c
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Modified: tor/trunk/src/common/torint.h
===================================================================
--- tor/trunk/src/common/torint.h	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/common/torint.h	2009-09-25 17:06:41 UTC (rev 20669)
@@ -117,7 +117,10 @@
 #ifndef INT32_MAX
 #define INT32_MAX 0x7fffffffu
 #endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
 #endif
+#endif
 
 #if (SIZEOF_LONG == 4)
 #ifndef HAVE_INT32_T

Modified: tor/trunk/src/common/tortls.c
===================================================================
--- tor/trunk/src/common/tortls.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/common/tortls.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -165,28 +165,49 @@
 #define _TOR_TLS_SYSCALL    (_MIN_TOR_TLS_ERROR_VAL - 2)
 #define _TOR_TLS_ZERORETURN (_MIN_TOR_TLS_ERROR_VAL - 1)
 
+#include "tortls_states.h"
+
+/** Return the symbolic name of an OpenSSL state. */
+static const char *
+ssl_state_to_string(int ssl_state)
+{
+  static char buf[40];
+  int i;
+  for (i = 0; state_map[i].name; ++i) {
+    if (state_map[i].state == ssl_state)
+      return state_map[i].name;
+  }
+  tor_snprintf(buf, sizeof(buf), "Unknown state %d", ssl_state);
+  return buf;
+}
+
 /** Log all pending tls errors at level <b>severity</b>.  Use
  * <b>doing</b> to describe our current activities.
  */
 static void
-tls_log_errors(tor_tls_t *tls, int severity, const char *doing)
+tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
 {
+  const char *state = NULL;
+  int st;
   unsigned long err;
   const char *msg, *lib, *func, *addr;
   addr = tls ? tls->address : NULL;
+  st = (tls && tls->ssl) ? tls->ssl->state : -1;
   while ((err = ERR_get_error()) != 0) {
     msg = (const char*)ERR_reason_error_string(err);
     lib = (const char*)ERR_lib_error_string(err);
     func = (const char*)ERR_func_error_string(err);
+    if (!state)
+      state = (st>=0)?ssl_state_to_string(st):"---";
     if (!msg) msg = "(null)";
     if (doing) {
-      log(severity, LD_NET, "TLS error while %s%s%s: %s (in %s:%s)",
+      log(severity, domain, "TLS error while %s%s%s: %s (in %s:%s:%s)",
           doing, addr?" with ":"", addr?addr:"",
-          msg, lib, func);
+          msg, lib, func, state);
     } else {
-      log(severity, LD_NET, "TLS error%s%s: %s (in %s:%s)",
+      log(severity, domain, "TLS error%s%s: %s (in %s:%s:%s)",
           addr?" with ":"", addr?addr:"",
-          msg, lib, func);
+          msg, lib, func, state);
     }
   }
 }
@@ -262,7 +283,7 @@
  */
 static int
 tor_tls_get_error(tor_tls_t *tls, int r, int extra,
-                  const char *doing, int severity)
+                  const char *doing, int severity, int domain)
 {
   int err = SSL_get_error(tls->ssl, r);
   int tor_error = TOR_TLS_ERROR_MISC;
@@ -277,25 +298,28 @@
       if (extra&CATCH_SYSCALL)
         return _TOR_TLS_SYSCALL;
       if (r == 0) {
-        log(severity, LD_NET, "TLS error: unexpected close while %s", doing);
+        log(severity, LD_NET, "TLS error: unexpected close while %s (%s)",
+            doing, ssl_state_to_string(tls->ssl->state));
         tor_error = TOR_TLS_ERROR_IO;
       } else {
         int e = tor_socket_errno(tls->socket);
         log(severity, LD_NET,
-            "TLS error: <syscall error while %s> (errno=%d: %s)",
-            doing, e, tor_socket_strerror(e));
+            "TLS error: <syscall error while %s> (errno=%d: %s; state=%s)",
+            doing, e, tor_socket_strerror(e),
+            ssl_state_to_string(tls->ssl->state));
         tor_error = tor_errno_to_tls_error(e);
       }
-      tls_log_errors(tls, severity, doing);
+      tls_log_errors(tls, severity, domain, doing);
       return tor_error;
     case SSL_ERROR_ZERO_RETURN:
       if (extra&CATCH_ZERO)
         return _TOR_TLS_ZERORETURN;
-      log(severity, LD_NET, "TLS connection closed while %s", doing);
-      tls_log_errors(tls, severity, doing);
+      log(severity, LD_NET, "TLS connection closed while %s in state %s",
+          doing, ssl_state_to_string(tls->ssl->state));
+      tls_log_errors(tls, severity, domain, doing);
       return TOR_TLS_CLOSE;
     default:
-      tls_log_errors(tls, severity, doing);
+      tls_log_errors(tls, severity, domain, doing);
       return TOR_TLS_ERROR_MISC;
   }
 }
@@ -427,7 +451,7 @@
     x509 = NULL;
   }
  done:
-  tls_log_errors(NULL, LOG_WARN, "generating certificate");
+  tls_log_errors(NULL, LOG_WARN, LD_NET, "generating certificate");
   if (sign_pkey)
     EVP_PKEY_free(sign_pkey);
   if (pkey)
@@ -615,7 +639,7 @@
   return 0;
 
  error:
-  tls_log_errors(NULL, LOG_WARN, "creating TLS context");
+  tls_log_errors(NULL, LOG_WARN, LD_NET, "creating TLS context");
   tor_free(nickname);
   tor_free(nn2);
   if (pkey)
@@ -811,7 +835,7 @@
 
   tor_assert(global_tls_context); /* make sure somebody made it first */
   if (!(result->ssl = SSL_new(global_tls_context->ctx))) {
-    tls_log_errors(NULL, LOG_WARN, "generating TLS context");
+    tls_log_errors(NULL, LOG_WARN, LD_NET, "generating TLS context");
     tor_free(result);
     return NULL;
   }
@@ -827,7 +851,10 @@
 
   if (!SSL_set_cipher_list(result->ssl,
                      isServer ? SERVER_CIPHER_LIST : CLIENT_CIPHER_LIST)) {
-    tls_log_errors(NULL, LOG_WARN, "setting ciphers");
+    tls_log_errors(NULL, LOG_WARN, LD_NET, "setting ciphers");
+#ifdef SSL_set_tlsext_host_name
+    SSL_set_tlsext_host_name(result->ssl, NULL);
+#endif
     SSL_free(result->ssl);
     tor_free(result);
     return NULL;
@@ -837,7 +864,10 @@
   result->socket = sock;
   bio = BIO_new_socket(sock, BIO_NOCLOSE);
   if (! bio) {
-    tls_log_errors(NULL, LOG_WARN, "opening BIO");
+    tls_log_errors(NULL, LOG_WARN, LD_NET, "opening BIO");
+#ifdef SSL_set_tlsext_host_name
+    SSL_set_tlsext_host_name(result->ssl, NULL);
+#endif
     SSL_free(result->ssl);
     tor_free(result);
     return NULL;
@@ -861,7 +891,7 @@
   }
 #endif
   /* Not expected to get called. */
-  tls_log_errors(NULL, LOG_WARN, "generating TLS context");
+  tls_log_errors(NULL, LOG_WARN, LD_NET, "generating TLS context");
   return result;
 }
 
@@ -918,6 +948,9 @@
   if (!removed) {
     log_warn(LD_BUG, "Freeing a TLS that was not in the ssl->tls map.");
   }
+#ifdef SSL_set_tlsext_host_name
+  SSL_set_tlsext_host_name(tls->ssl, NULL);
+#endif
   SSL_free(tls->ssl);
   tls->ssl = NULL;
   tls->negotiated_callback = NULL;
@@ -953,7 +986,7 @@
 #endif
     return r;
   }
-  err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG);
+  err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET);
   if (err == _TOR_TLS_ZERORETURN || err == TOR_TLS_CLOSE) {
     log_debug(LD_NET,"read returned r=%d; TLS is closed",r);
     tls->state = TOR_TLS_ST_CLOSED;
@@ -989,7 +1022,7 @@
     tls->wantwrite_n = 0;
   }
   r = SSL_write(tls->ssl, cp, (int)n);
-  err = tor_tls_get_error(tls, r, 0, "writing", LOG_INFO);
+  err = tor_tls_get_error(tls, r, 0, "writing", LOG_INFO, LD_NET);
   if (err == TOR_TLS_DONE) {
     return r;
   }
@@ -1007,18 +1040,27 @@
 tor_tls_handshake(tor_tls_t *tls)
 {
   int r;
+  int oldstate;
   tor_assert(tls);
   tor_assert(tls->ssl);
   tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE);
   check_no_tls_errors();
+  oldstate = tls->ssl->state;
   if (tls->isServer) {
+    log_debug(LD_HANDSHAKE, "About to call SSL_accept on %p (%s)", tls,
+              ssl_state_to_string(tls->ssl->state));
     r = SSL_accept(tls->ssl);
   } else {
+    log_debug(LD_HANDSHAKE, "About to call SSL_connect on %p (%s)", tls,
+              ssl_state_to_string(tls->ssl->state));
     r = SSL_connect(tls->ssl);
   }
-  r = tor_tls_get_error(tls,r,0, "handshaking", LOG_INFO);
+  if (oldstate != tls->ssl->state)
+    log_debug(LD_HANDSHAKE, "After call, %p was in state %s",
+              tls, ssl_state_to_string(tls->ssl->state));
+  r = tor_tls_get_error(tls,r,0, "handshaking", LOG_INFO, LD_HANDSHAKE);
   if (ERR_peek_error() != 0) {
-    tls_log_errors(tls, tls->isServer ? LOG_INFO : LOG_WARN,
+    tls_log_errors(tls, tls->isServer ? LOG_INFO : LOG_WARN, LD_HANDSHAKE,
                    "handshaking");
     return TOR_TLS_ERROR_MISC;
   }
@@ -1039,7 +1081,8 @@
                    " get set. Fixing that.");
         }
         tls->wasV2Handshake = 1;
-        log_debug(LD_NET, "Completed V2 TLS handshake with client; waiting "
+        log_debug(LD_HANDSHAKE,
+                  "Completed V2 TLS handshake with client; waiting "
                   "for renegotiation.");
       } else {
         tls->wasV2Handshake = 0;
@@ -1051,10 +1094,13 @@
       X509 *cert = SSL_get_peer_certificate(tls->ssl);
       STACK_OF(X509) *chain = SSL_get_peer_cert_chain(tls->ssl);
       int n_certs = sk_X509_num(chain);
-      if (n_certs > 1 || (n_certs == 1 && cert != sk_X509_value(chain, 0)))
+      if (n_certs > 1 || (n_certs == 1 && cert != sk_X509_value(chain, 0))) {
+        log_debug(LD_HANDSHAKE, "Server sent back multiple certificates; it "
+                  "looks like a v1 handshake on %p", tls);
         tls->wasV2Handshake = 0;
-      else {
-        log_debug(LD_NET, "Server sent back a single certificate; looks like "
+      } else {
+        log_debug(LD_HANDSHAKE,
+                  "Server sent back a single certificate; looks like "
                   "a v2 handshake on %p.", tls);
         tls->wasV2Handshake = 1;
       }
@@ -1062,7 +1108,7 @@
         X509_free(cert);
 #endif
       if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) {
-        tls_log_errors(NULL, LOG_WARN, "re-setting ciphers");
+        tls_log_errors(NULL, LOG_WARN, LD_HANDSHAKE, "re-setting ciphers");
         r = TOR_TLS_ERROR_MISC;
       }
     }
@@ -1085,7 +1131,8 @@
   if (tls->state != TOR_TLS_ST_RENEGOTIATE) {
     int r = SSL_renegotiate(tls->ssl);
     if (r <= 0) {
-      return tor_tls_get_error(tls, r, 0, "renegotiating", LOG_WARN);
+      return tor_tls_get_error(tls, r, 0, "renegotiating", LOG_WARN,
+                               LD_HANDSHAKE);
     }
     tls->state = TOR_TLS_ST_RENEGOTIATE;
   }
@@ -1094,7 +1141,8 @@
     tls->state = TOR_TLS_ST_OPEN;
     return TOR_TLS_DONE;
   } else
-    return tor_tls_get_error(tls, r, 0, "renegotiating handshake", LOG_INFO);
+    return tor_tls_get_error(tls, r, 0, "renegotiating handshake", LOG_INFO,
+                             LD_HANDSHAKE);
 }
 
 /** Shut down an open tls connection <b>tls</b>.  When finished, returns
@@ -1118,7 +1166,7 @@
         r = SSL_read(tls->ssl, buf, 128);
       } while (r>0);
       err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading to shut down",
-                              LOG_INFO);
+                              LOG_INFO, LD_NET);
       if (err == _TOR_TLS_ZERORETURN) {
         tls->state = TOR_TLS_ST_GOTCLOSE;
         /* fall through... */
@@ -1134,7 +1182,7 @@
       return TOR_TLS_DONE;
     }
     err = tor_tls_get_error(tls, r, CATCH_SYSCALL|CATCH_ZERO, "shutting down",
-                            LOG_INFO);
+                            LOG_INFO, LD_NET);
     if (err == _TOR_TLS_SYSCALL) {
       /* The underlying TCP connection closed while we were shutting down. */
       tls->state = TOR_TLS_ST_CLOSED;
@@ -1166,7 +1214,7 @@
 {
   X509 *cert;
   cert = SSL_get_peer_certificate(tls->ssl);
-  tls_log_errors(tls, LOG_WARN, "getting peer certificate");
+  tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate");
   if (!cert)
     return 0;
   X509_free(cert);
@@ -1193,7 +1241,7 @@
     log_warn(LD_GENERAL, "Couldn't allocate BIO!"); goto end;
   }
   if (!(ASN1_TIME_print(bio, X509_get_notBefore(cert)))) {
-    tls_log_errors(NULL, LOG_WARN, "printing certificate lifetime");
+    tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime");
     goto end;
   }
   BIO_get_mem_ptr(bio, &buf);
@@ -1201,7 +1249,7 @@
 
   (void)BIO_reset(bio);
   if (!(ASN1_TIME_print(bio, X509_get_notAfter(cert)))) {
-    tls_log_errors(NULL, LOG_WARN, "printing certificate lifetime");
+    tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime");
     goto end;
   }
   BIO_get_mem_ptr(bio, &buf);
@@ -1215,7 +1263,7 @@
 
  end:
   /* Not expected to get invoked */
-  tls_log_errors(NULL, LOG_WARN, "getting certificate lifetime");
+  tls_log_errors(NULL, LOG_WARN, LD_NET, "getting certificate lifetime");
   if (bio)
     BIO_free(bio);
   if (s1)
@@ -1289,7 +1337,7 @@
   if (!(id_pkey = X509_get_pubkey(id_cert)) ||
       X509_verify(cert, id_pkey) <= 0) {
     log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0");
-    tls_log_errors(tls, severity,"verifying certificate");
+    tls_log_errors(tls, severity, LD_HANDSHAKE, "verifying certificate");
     goto done;
   }
 
@@ -1308,7 +1356,7 @@
 
   /* This should never get invoked, but let's make sure in case OpenSSL
    * acts unexpectedly. */
-  tls_log_errors(tls, LOG_WARN, "finishing tor_tls_verify");
+  tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "finishing tor_tls_verify");
 
   return r;
 }
@@ -1347,7 +1395,7 @@
   if (cert)
     X509_free(cert);
   /* Not expected to get invoked */
-  tls_log_errors(tls, LOG_WARN, "checking certificate lifetime");
+  tls_log_errors(tls, LOG_WARN, LD_NET, "checking certificate lifetime");
 
   return r;
 }
@@ -1415,7 +1463,7 @@
     return;
   log(LOG_WARN, LD_CRYPTO, "Unhandled OpenSSL errors found at %s:%d: ",
       tor_fix_source_file(fname), line);
-  tls_log_errors(NULL, LOG_WARN, NULL);
+  tls_log_errors(NULL, LOG_WARN, LD_NET, NULL);
 }
 
 /** Return true iff the initial TLS connection at <b>tls</b> did not use a v2
@@ -1442,8 +1490,8 @@
  * buffer and *<b>wbuf_bytes</b> to the amount actually used. */
 void
 tor_tls_get_buffer_sizes(tor_tls_t *tls,
-                         int *rbuf_capacity, int *rbuf_bytes,
-                         int *wbuf_capacity, int *wbuf_bytes)
+                         size_t *rbuf_capacity, size_t *rbuf_bytes,
+                         size_t *wbuf_capacity, size_t *wbuf_bytes)
 {
   if (tls->ssl->s3->rbuf.buf)
     *rbuf_capacity = tls->ssl->s3->rbuf.len;

Modified: tor/trunk/src/common/tortls.h
===================================================================
--- tor/trunk/src/common/tortls.h	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/common/tortls.h	2009-09-25 17:06:41 UTC (rev 20669)
@@ -73,8 +73,8 @@
                              size_t *n_read, size_t *n_written);
 
 void tor_tls_get_buffer_sizes(tor_tls_t *tls,
-                              int *rbuf_capacity, int *rbuf_bytes,
-                              int *wbuf_capacity, int *wbuf_bytes);
+                              size_t *rbuf_capacity, size_t *rbuf_bytes,
+                              size_t *wbuf_capacity, size_t *wbuf_bytes);
 
 int tor_tls_used_v1_handshake(tor_tls_t *tls);
 

Added: tor/trunk/src/common/tortls_states.h
===================================================================
--- tor/trunk/src/common/tortls_states.h	                        (rev 0)
+++ tor/trunk/src/common/tortls_states.h	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,393 @@
+/* Copyright (c) 2003, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2009, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef _TORTLS_STATES_H
+#define _TORTLS_STATES_H
+
+static const struct { int state; const char *name; } state_map[] = {
+#define S(state) { state, #state }
+#ifdef DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A
+  S(DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A),
+#endif
+#ifdef DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B
+  S(DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B),
+#endif
+#ifdef DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A
+  S(DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A),
+#endif
+#ifdef DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B
+  S(DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B),
+#endif
+#ifdef SSL23_ST_CR_SRVR_HELLO_A
+  S(SSL23_ST_CR_SRVR_HELLO_A),
+#endif
+#ifdef SSL23_ST_CR_SRVR_HELLO_B
+  S(SSL23_ST_CR_SRVR_HELLO_B),
+#endif
+#ifdef SSL23_ST_CW_CLNT_HELLO_A
+  S(SSL23_ST_CW_CLNT_HELLO_A),
+#endif
+#ifdef SSL23_ST_CW_CLNT_HELLO_B
+  S(SSL23_ST_CW_CLNT_HELLO_B),
+#endif
+#ifdef SSL23_ST_SR_CLNT_HELLO_A
+  S(SSL23_ST_SR_CLNT_HELLO_A),
+#endif
+#ifdef SSL23_ST_SR_CLNT_HELLO_B
+  S(SSL23_ST_SR_CLNT_HELLO_B),
+#endif
+#ifdef SSL2_ST_CLIENT_START_ENCRYPTION
+  S(SSL2_ST_CLIENT_START_ENCRYPTION),
+#endif
+#ifdef SSL2_ST_GET_CLIENT_FINISHED_A
+  S(SSL2_ST_GET_CLIENT_FINISHED_A),
+#endif
+#ifdef SSL2_ST_GET_CLIENT_FINISHED_B
+  S(SSL2_ST_GET_CLIENT_FINISHED_B),
+#endif
+#ifdef SSL2_ST_GET_CLIENT_HELLO_A
+  S(SSL2_ST_GET_CLIENT_HELLO_A),
+#endif
+#ifdef SSL2_ST_GET_CLIENT_HELLO_B
+  S(SSL2_ST_GET_CLIENT_HELLO_B),
+#endif
+#ifdef SSL2_ST_GET_CLIENT_HELLO_C
+  S(SSL2_ST_GET_CLIENT_HELLO_C),
+#endif
+#ifdef SSL2_ST_GET_CLIENT_MASTER_KEY_A
+  S(SSL2_ST_GET_CLIENT_MASTER_KEY_A),
+#endif
+#ifdef SSL2_ST_GET_CLIENT_MASTER_KEY_B
+  S(SSL2_ST_GET_CLIENT_MASTER_KEY_B),
+#endif
+#ifdef SSL2_ST_GET_SERVER_FINISHED_A
+  S(SSL2_ST_GET_SERVER_FINISHED_A),
+#endif
+#ifdef SSL2_ST_GET_SERVER_FINISHED_B
+  S(SSL2_ST_GET_SERVER_FINISHED_B),
+#endif
+#ifdef SSL2_ST_GET_SERVER_HELLO_A
+  S(SSL2_ST_GET_SERVER_HELLO_A),
+#endif
+#ifdef SSL2_ST_GET_SERVER_HELLO_B
+  S(SSL2_ST_GET_SERVER_HELLO_B),
+#endif
+#ifdef SSL2_ST_GET_SERVER_VERIFY_A
+  S(SSL2_ST_GET_SERVER_VERIFY_A),
+#endif
+#ifdef SSL2_ST_GET_SERVER_VERIFY_B
+  S(SSL2_ST_GET_SERVER_VERIFY_B),
+#endif
+#ifdef SSL2_ST_SEND_CLIENT_CERTIFICATE_A
+  S(SSL2_ST_SEND_CLIENT_CERTIFICATE_A),
+#endif
+#ifdef SSL2_ST_SEND_CLIENT_CERTIFICATE_B
+  S(SSL2_ST_SEND_CLIENT_CERTIFICATE_B),
+#endif
+#ifdef SSL2_ST_SEND_CLIENT_CERTIFICATE_C
+  S(SSL2_ST_SEND_CLIENT_CERTIFICATE_C),
+#endif
+#ifdef SSL2_ST_SEND_CLIENT_CERTIFICATE_D
+  S(SSL2_ST_SEND_CLIENT_CERTIFICATE_D),
+#endif
+#ifdef SSL2_ST_SEND_CLIENT_FINISHED_A
+  S(SSL2_ST_SEND_CLIENT_FINISHED_A),
+#endif
+#ifdef SSL2_ST_SEND_CLIENT_FINISHED_B
+  S(SSL2_ST_SEND_CLIENT_FINISHED_B),
+#endif
+#ifdef SSL2_ST_SEND_CLIENT_HELLO_A
+  S(SSL2_ST_SEND_CLIENT_HELLO_A),
+#endif
+#ifdef SSL2_ST_SEND_CLIENT_HELLO_B
+  S(SSL2_ST_SEND_CLIENT_HELLO_B),
+#endif
+#ifdef SSL2_ST_SEND_CLIENT_MASTER_KEY_A
+  S(SSL2_ST_SEND_CLIENT_MASTER_KEY_A),
+#endif
+#ifdef SSL2_ST_SEND_CLIENT_MASTER_KEY_B
+  S(SSL2_ST_SEND_CLIENT_MASTER_KEY_B),
+#endif
+#ifdef SSL2_ST_SEND_REQUEST_CERTIFICATE_A
+  S(SSL2_ST_SEND_REQUEST_CERTIFICATE_A),
+#endif
+#ifdef SSL2_ST_SEND_REQUEST_CERTIFICATE_B
+  S(SSL2_ST_SEND_REQUEST_CERTIFICATE_B),
+#endif
+#ifdef SSL2_ST_SEND_REQUEST_CERTIFICATE_C
+  S(SSL2_ST_SEND_REQUEST_CERTIFICATE_C),
+#endif
+#ifdef SSL2_ST_SEND_REQUEST_CERTIFICATE_D
+  S(SSL2_ST_SEND_REQUEST_CERTIFICATE_D),
+#endif
+#ifdef SSL2_ST_SEND_SERVER_FINISHED_A
+  S(SSL2_ST_SEND_SERVER_FINISHED_A),
+#endif
+#ifdef SSL2_ST_SEND_SERVER_FINISHED_B
+  S(SSL2_ST_SEND_SERVER_FINISHED_B),
+#endif
+#ifdef SSL2_ST_SEND_SERVER_HELLO_A
+  S(SSL2_ST_SEND_SERVER_HELLO_A),
+#endif
+#ifdef SSL2_ST_SEND_SERVER_HELLO_B
+  S(SSL2_ST_SEND_SERVER_HELLO_B),
+#endif
+#ifdef SSL2_ST_SEND_SERVER_VERIFY_A
+  S(SSL2_ST_SEND_SERVER_VERIFY_A),
+#endif
+#ifdef SSL2_ST_SEND_SERVER_VERIFY_B
+  S(SSL2_ST_SEND_SERVER_VERIFY_B),
+#endif
+#ifdef SSL2_ST_SEND_SERVER_VERIFY_C
+  S(SSL2_ST_SEND_SERVER_VERIFY_C),
+#endif
+#ifdef SSL2_ST_SERVER_START_ENCRYPTION
+  S(SSL2_ST_SERVER_START_ENCRYPTION),
+#endif
+#ifdef SSL2_ST_X509_GET_CLIENT_CERTIFICATE
+  S(SSL2_ST_X509_GET_CLIENT_CERTIFICATE),
+#endif
+#ifdef SSL2_ST_X509_GET_SERVER_CERTIFICATE
+  S(SSL2_ST_X509_GET_SERVER_CERTIFICATE),
+#endif
+#ifdef SSL3_ST_CR_CERT_A
+  S(SSL3_ST_CR_CERT_A),
+#endif
+#ifdef SSL3_ST_CR_CERT_B
+  S(SSL3_ST_CR_CERT_B),
+#endif
+#ifdef SSL3_ST_CR_CERT_REQ_A
+  S(SSL3_ST_CR_CERT_REQ_A),
+#endif
+#ifdef SSL3_ST_CR_CERT_REQ_B
+  S(SSL3_ST_CR_CERT_REQ_B),
+#endif
+#ifdef SSL3_ST_CR_CERT_STATUS_A
+  S(SSL3_ST_CR_CERT_STATUS_A),
+#endif
+#ifdef SSL3_ST_CR_CERT_STATUS_B
+  S(SSL3_ST_CR_CERT_STATUS_B),
+#endif
+#ifdef SSL3_ST_CR_CHANGE_A
+  S(SSL3_ST_CR_CHANGE_A),
+#endif
+#ifdef SSL3_ST_CR_CHANGE_B
+  S(SSL3_ST_CR_CHANGE_B),
+#endif
+#ifdef SSL3_ST_CR_FINISHED_A
+  S(SSL3_ST_CR_FINISHED_A),
+#endif
+#ifdef SSL3_ST_CR_FINISHED_B
+  S(SSL3_ST_CR_FINISHED_B),
+#endif
+#ifdef SSL3_ST_CR_KEY_EXCH_A
+  S(SSL3_ST_CR_KEY_EXCH_A),
+#endif
+#ifdef SSL3_ST_CR_KEY_EXCH_B
+  S(SSL3_ST_CR_KEY_EXCH_B),
+#endif
+#ifdef SSL3_ST_CR_SESSION_TICKET_A
+  S(SSL3_ST_CR_SESSION_TICKET_A),
+#endif
+#ifdef SSL3_ST_CR_SESSION_TICKET_B
+  S(SSL3_ST_CR_SESSION_TICKET_B),
+#endif
+#ifdef SSL3_ST_CR_SRVR_DONE_A
+  S(SSL3_ST_CR_SRVR_DONE_A),
+#endif
+#ifdef SSL3_ST_CR_SRVR_DONE_B
+  S(SSL3_ST_CR_SRVR_DONE_B),
+#endif
+#ifdef SSL3_ST_CR_SRVR_HELLO_A
+  S(SSL3_ST_CR_SRVR_HELLO_A),
+#endif
+#ifdef SSL3_ST_CR_SRVR_HELLO_B
+  S(SSL3_ST_CR_SRVR_HELLO_B),
+#endif
+#ifdef SSL3_ST_CW_CERT_A
+  S(SSL3_ST_CW_CERT_A),
+#endif
+#ifdef SSL3_ST_CW_CERT_B
+  S(SSL3_ST_CW_CERT_B),
+#endif
+#ifdef SSL3_ST_CW_CERT_C
+  S(SSL3_ST_CW_CERT_C),
+#endif
+#ifdef SSL3_ST_CW_CERT_D
+  S(SSL3_ST_CW_CERT_D),
+#endif
+#ifdef SSL3_ST_CW_CERT_VRFY_A
+  S(SSL3_ST_CW_CERT_VRFY_A),
+#endif
+#ifdef SSL3_ST_CW_CERT_VRFY_B
+  S(SSL3_ST_CW_CERT_VRFY_B),
+#endif
+#ifdef SSL3_ST_CW_CHANGE_A
+  S(SSL3_ST_CW_CHANGE_A),
+#endif
+#ifdef SSL3_ST_CW_CHANGE_B
+  S(SSL3_ST_CW_CHANGE_B),
+#endif
+#ifdef SSL3_ST_CW_CLNT_HELLO_A
+  S(SSL3_ST_CW_CLNT_HELLO_A),
+#endif
+#ifdef SSL3_ST_CW_CLNT_HELLO_B
+  S(SSL3_ST_CW_CLNT_HELLO_B),
+#endif
+#ifdef SSL3_ST_CW_FINISHED_A
+  S(SSL3_ST_CW_FINISHED_A),
+#endif
+#ifdef SSL3_ST_CW_FINISHED_B
+  S(SSL3_ST_CW_FINISHED_B),
+#endif
+#ifdef SSL3_ST_CW_FLUSH
+  S(SSL3_ST_CW_FLUSH),
+#endif
+#ifdef SSL3_ST_CW_KEY_EXCH_A
+  S(SSL3_ST_CW_KEY_EXCH_A),
+#endif
+#ifdef SSL3_ST_CW_KEY_EXCH_B
+  S(SSL3_ST_CW_KEY_EXCH_B),
+#endif
+#ifdef SSL3_ST_SR_CERT_A
+  S(SSL3_ST_SR_CERT_A),
+#endif
+#ifdef SSL3_ST_SR_CERT_B
+  S(SSL3_ST_SR_CERT_B),
+#endif
+#ifdef SSL3_ST_SR_CERT_VRFY_A
+  S(SSL3_ST_SR_CERT_VRFY_A),
+#endif
+#ifdef SSL3_ST_SR_CERT_VRFY_B
+  S(SSL3_ST_SR_CERT_VRFY_B),
+#endif
+#ifdef SSL3_ST_SR_CHANGE_A
+  S(SSL3_ST_SR_CHANGE_A),
+#endif
+#ifdef SSL3_ST_SR_CHANGE_B
+  S(SSL3_ST_SR_CHANGE_B),
+#endif
+#ifdef SSL3_ST_SR_CLNT_HELLO_A
+  S(SSL3_ST_SR_CLNT_HELLO_A),
+#endif
+#ifdef SSL3_ST_SR_CLNT_HELLO_B
+  S(SSL3_ST_SR_CLNT_HELLO_B),
+#endif
+#ifdef SSL3_ST_SR_CLNT_HELLO_C
+  S(SSL3_ST_SR_CLNT_HELLO_C),
+#endif
+#ifdef SSL3_ST_SR_FINISHED_A
+  S(SSL3_ST_SR_FINISHED_A),
+#endif
+#ifdef SSL3_ST_SR_FINISHED_B
+  S(SSL3_ST_SR_FINISHED_B),
+#endif
+#ifdef SSL3_ST_SR_KEY_EXCH_A
+  S(SSL3_ST_SR_KEY_EXCH_A),
+#endif
+#ifdef SSL3_ST_SR_KEY_EXCH_B
+  S(SSL3_ST_SR_KEY_EXCH_B),
+#endif
+#ifdef SSL3_ST_SW_CERT_A
+  S(SSL3_ST_SW_CERT_A),
+#endif
+#ifdef SSL3_ST_SW_CERT_B
+  S(SSL3_ST_SW_CERT_B),
+#endif
+#ifdef SSL3_ST_SW_CERT_REQ_A
+  S(SSL3_ST_SW_CERT_REQ_A),
+#endif
+#ifdef SSL3_ST_SW_CERT_REQ_B
+  S(SSL3_ST_SW_CERT_REQ_B),
+#endif
+#ifdef SSL3_ST_SW_CERT_STATUS_A
+  S(SSL3_ST_SW_CERT_STATUS_A),
+#endif
+#ifdef SSL3_ST_SW_CERT_STATUS_B
+  S(SSL3_ST_SW_CERT_STATUS_B),
+#endif
+#ifdef SSL3_ST_SW_CHANGE_A
+  S(SSL3_ST_SW_CHANGE_A),
+#endif
+#ifdef SSL3_ST_SW_CHANGE_B
+  S(SSL3_ST_SW_CHANGE_B),
+#endif
+#ifdef SSL3_ST_SW_FINISHED_A
+  S(SSL3_ST_SW_FINISHED_A),
+#endif
+#ifdef SSL3_ST_SW_FINISHED_B
+  S(SSL3_ST_SW_FINISHED_B),
+#endif
+#ifdef SSL3_ST_SW_FLUSH
+  S(SSL3_ST_SW_FLUSH),
+#endif
+#ifdef SSL3_ST_SW_HELLO_REQ_A
+  S(SSL3_ST_SW_HELLO_REQ_A),
+#endif
+#ifdef SSL3_ST_SW_HELLO_REQ_B
+  S(SSL3_ST_SW_HELLO_REQ_B),
+#endif
+#ifdef SSL3_ST_SW_HELLO_REQ_C
+  S(SSL3_ST_SW_HELLO_REQ_C),
+#endif
+#ifdef SSL3_ST_SW_KEY_EXCH_A
+  S(SSL3_ST_SW_KEY_EXCH_A),
+#endif
+#ifdef SSL3_ST_SW_KEY_EXCH_B
+  S(SSL3_ST_SW_KEY_EXCH_B),
+#endif
+#ifdef SSL3_ST_SW_SESSION_TICKET_A
+  S(SSL3_ST_SW_SESSION_TICKET_A),
+#endif
+#ifdef SSL3_ST_SW_SESSION_TICKET_B
+  S(SSL3_ST_SW_SESSION_TICKET_B),
+#endif
+#ifdef SSL3_ST_SW_SRVR_DONE_A
+  S(SSL3_ST_SW_SRVR_DONE_A),
+#endif
+#ifdef SSL3_ST_SW_SRVR_DONE_B
+  S(SSL3_ST_SW_SRVR_DONE_B),
+#endif
+#ifdef SSL3_ST_SW_SRVR_HELLO_A
+  S(SSL3_ST_SW_SRVR_HELLO_A),
+#endif
+#ifdef SSL3_ST_SW_SRVR_HELLO_B
+  S(SSL3_ST_SW_SRVR_HELLO_B),
+#endif
+#ifdef SSL_ST_ACCEPT
+  S(SSL_ST_ACCEPT),
+#endif
+#ifdef SSL_ST_BEFORE
+  S(SSL_ST_BEFORE),
+#endif
+#ifdef SSL_ST_CONNECT
+  S(SSL_ST_CONNECT),
+#endif
+#ifdef SSL_ST_INIT
+  S(SSL_ST_INIT),
+#endif
+#ifdef SSL_ST_MASK
+  S(SSL_ST_MASK),
+#endif
+#ifdef SSL_ST_OK
+  S(SSL_ST_OK),
+#endif
+#ifdef SSL_ST_READ_BODY
+  S(SSL_ST_READ_BODY),
+#endif
+#ifdef SSL_ST_READ_DONE
+  S(SSL_ST_READ_DONE),
+#endif
+#ifdef SSL_ST_READ_HEADER
+  S(SSL_ST_READ_HEADER),
+#endif
+#ifdef SSL_ST_RENEGOTIATE
+  S(SSL_ST_RENEGOTIATE),
+#endif
+  { 0, NULL }
+};
+
+#endif
+


Property changes on: tor/trunk/src/common/tortls_states.h
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Modified: tor/trunk/src/common/util.c
===================================================================
--- tor/trunk/src/common/util.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/common/util.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -735,7 +735,18 @@
   CHECK_STRTOX_RESULT();
 }
 
-/** As tor_parse_log, but return a unit64_t.  Only base 10 is guaranteed to
+/** As tor_parse_long(), but return a double. */
+double
+tor_parse_double(const char *s, double min, double max, int *ok, char **next)
+{
+  char *endptr;
+  double r;
+
+  r = strtod(s, &endptr);
+  CHECK_STRTOX_RESULT();
+}
+
+/** As tor_parse_long, but return a uint64_t.  Only base 10 is guaranteed to
  * work for now. */
 uint64_t
 tor_parse_uint64(const char *s, int base, uint64_t min,
@@ -1023,6 +1034,42 @@
  * Time
  * ===== */
 
+/**
+ * Converts struct timeval to a double value.
+ * Preserves microsecond precision, but just barely.
+ * Error is approx +/- 0.1 usec when dealing with epoch values.
+ */
+double
+tv_to_double(const struct timeval *tv)
+{
+  double conv = tv->tv_sec;
+  conv += tv->tv_usec/1000000.0;
+  return conv;
+}
+
+/**
+ * Converts timeval to milliseconds.
+ */
+int64_t
+tv_to_msec(const struct timeval *tv)
+{
+  int64_t conv = ((int64_t)tv->tv_sec)*1000L;
+  /* Round ghetto-style */
+  conv += ((int64_t)tv->tv_usec+500)/1000L;
+  return conv;
+}
+
+/**
+ * Converts timeval to microseconds.
+ */
+int64_t
+tv_to_usec(const struct timeval *tv)
+{
+  int64_t conv = ((int64_t)tv->tv_sec)*1000000L;
+  conv += tv->tv_usec;
+  return conv;
+}
+
 /** Return the number of microseconds elapsed between *start and *end.
  */
 long
@@ -1055,7 +1102,9 @@
     return LONG_MAX;
   }
 
-  mdiff = secdiff*1000L + (end->tv_usec - start->tv_usec) / 1000L;
+  /* Subtract and round */
+  mdiff = secdiff*1000L +
+      ((long)end->tv_usec - (long)start->tv_usec + 500L) / 1000L;
   return mdiff;
 }
 
@@ -1865,7 +1914,8 @@
                           int open_flags)
 {
   open_file_t *file = NULL;
-  int fd, result;
+  int fd;
+  ssize_t result;
   fd = start_writing_to_file(fname, open_flags, 0600, &file);
   if (fd<0)
     return -1;
@@ -1950,7 +2000,7 @@
   int fd; /* router file */
   struct stat statbuf;
   char *string;
-  int r;
+  ssize_t r;
   int bin = flags & RFTS_BIN;
 
   tor_assert(filename);
@@ -2009,7 +2059,7 @@
        * match for size. */
       int save_errno = errno;
       log_warn(LD_FS,"Could read only %d of %ld bytes of file \"%s\".",
-               r, (long)statbuf.st_size,filename);
+               (int)r, (long)statbuf.st_size,filename);
       tor_free(string);
       close(fd);
       errno = save_errno;

Modified: tor/trunk/src/common/util.h
===================================================================
--- tor/trunk/src/common/util.h	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/common/util.h	2009-09-25 17:06:41 UTC (rev 20669)
@@ -182,6 +182,8 @@
                     long max, int *ok, char **next);
 unsigned long tor_parse_ulong(const char *s, int base, unsigned long min,
                               unsigned long max, int *ok, char **next);
+double tor_parse_double(const char *s, double min, double max, int *ok,
+                        char **next);
 uint64_t tor_parse_uint64(const char *s, int base, uint64_t min,
                          uint64_t max, int *ok, char **next);
 const char *hex_str(const char *from, size_t fromlen) ATTR_NONNULL((1));
@@ -210,6 +212,9 @@
 int base16_decode(char *dest, size_t destlen, const char *src, size_t srclen);
 
 /* Time helpers */
+double tv_to_double(const struct timeval *tv);
+int64_t tv_to_msec(const struct timeval *tv);
+int64_t tv_to_usec(const struct timeval *tv);
 long tv_udiff(const struct timeval *start, const struct timeval *end);
 long tv_mdiff(const struct timeval *start, const struct timeval *end);
 time_t tor_timegm(struct tm *tm);

Modified: tor/trunk/src/or/Makefile.am
===================================================================
--- tor/trunk/src/or/Makefile.am	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/Makefile.am	2009-09-25 17:06:41 UTC (rev 20669)
@@ -1,8 +1,5 @@
-TESTS = test
-
-noinst_PROGRAMS = test
-
 bin_PROGRAMS = tor
+noinst_LIBRARIES = libtor.a
 
 if BUILD_NT_SERVICES
 tor_platform_source=ntmain.c
@@ -18,7 +15,7 @@
 evdns_source=eventdns.c
 endif
 
-COMMON_SRC = buffers.c circuitbuild.c circuitlist.c \
+libtor_a_SOURCES = buffers.c circuitbuild.c circuitlist.c \
 	circuituse.c command.c config.c \
 	connection.c connection_edge.c connection_or.c control.c \
 	cpuworker.c directory.c dirserv.c dirvote.c \
@@ -28,8 +25,12 @@
 	rendservice.c rephist.c router.c routerlist.c routerparse.c \
 	$(evdns_source) config_codedigest.c
 
-tor_SOURCES = $(COMMON_SRC) tor_main.c
+#libtor_a_LIBADD = ../common/libor.a ../common/libor-crypto.a \
+#	../common/libor-event.a
 
+
+tor_SOURCES = tor_main.c
+
 AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
         -DLOCALSTATEDIR="\"$(localstatedir)\"" \
         -DBINDIR="\"$(bindir)\""
@@ -39,17 +40,10 @@
 # matters a lot there, and is quite hard to debug if you forget to do it.
 
 tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
-tor_LDADD = ../common/libor.a ../common/libor-crypto.a \
+tor_LDADD = ./libtor.a ../common/libor.a ../common/libor-crypto.a \
 	../common/libor-event.a \
-	-lz -levent -lssl -lcrypto @TOR_LIB_WS32@ @TOR_LIB_GDI@
-test_SOURCES = $(COMMON_SRC) test_data.c test.c
+	-lz -lm -levent -lssl -lcrypto @TOR_LIB_WS32@ @TOR_LIB_GDI@
 
-test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
-        @TOR_LDFLAGS_libevent@
-test_LDADD = ../common/libor.a ../common/libor-crypto.a \
-	../common/libor-event.a \
-	-lz -levent -lssl -lcrypto @TOR_LIB_WS32@ @TOR_LIB_GDI@
-
 noinst_HEADERS = or.h eventdns.h eventdns_tor.h micro-revision.i
 
 config_codedigest.o: or_sha1.i
@@ -57,65 +51,29 @@
 tor_main.o: micro-revision.i
 
 micro-revision.i: FORCE
-	@svkdir=$$SVKROOT; 					\
-	if test "x$$svkdir" = x ; then 				\
-	  svkdir=$$HOME/.svk; 					\
-	fi; 							\
-	if test -d ../../.git && test -x "`which git 2>&1;true`" ; then \
-	  if test -d ../../.git/svn && test -x "`which git-svn 2>&1;true`" ; then \
-	    git-svn info ../../README | 			\
-	    sed -n 's/^Revision: \([0-9][0-9]*\).*/"\1"/p'      \
-	                                   > micro-revision.tmp \
-	        || true; 					\
-	  fi; 							\
-	elif test -d ../../.svn && test -x "`which svn 2>&1;true`" ; then \
-	  svn info ../.. |					\
-	  sed -n 's/^Revision: \([0-9][0-9]*\).*/"\1"/p' > micro-revision.tmp \
-	     || true;						\
-	elif test -x "`which svk 2>&1;true`" && test -d $$svkdir/local; then \
-	  location=../..;					\
-	  rev=x;						\
-	  while test x$$rev = xx; do				\
-	    x=`svk info $$location |				\
-	      sed -n 's/^Mirrored From:.*, Rev\. \([0-9][0-9]*\)/\1/p'`; \
-	    if test x$$x != x; then				\
-	      rev=$$x;						\
-	      break;						\
-	    else						\
-	      loc=`svk info $$location |			\
-		sed -n 's/^Copied From: \(.*\), Rev\. [0-9][0-9]*/\1/p' | \
-	        head -1`;					\
-	      if test x$$loc = x; then				\
-		break;						\
-	      else						\
-		location=/$$loc;				\
-	      fi;						\
-	    fi;							\
-	  done;							\
-	  if test x$$rev != xx; then				\
-	    echo \"$$rev\" > micro-revision.tmp;		\
-	  fi;							\
-	fi;							\
-	if test ! -f micro-revision.tmp ; then			\
-	  if test ! -f micro-revision.i ; then			\
-	    echo '""' > micro-revision.i;			\
-	  fi;							\
-	elif test ! -f micro-revision.i ||			\
+	@rm -f micro-revision.tmp;					\
+	if test -d ../../.git && test -x "`which git 2>&1;true`"; then 	\
+	  HASH="`git rev-parse --short=16 HEAD`"; 			\
+	  echo \"$$HASH\" > micro-revision.tmp; 			\
+        fi;								\
+	if test ! -f micro-revision.tmp ; then				\
+	  if test ! -f micro-revision.i ; then				\
+	    echo '""' > micro-revision.i;				\
+	  fi;								\
+	elif test ! -f micro-revision.i ||				\
 	  test x"`cat micro-revision.tmp`" != x"`cat micro-revision.i`"; then \
-	  mv micro-revision.tmp micro-revision.i;		\
+	  mv micro-revision.tmp micro-revision.i;			\
 	fi; true
 
-or_sha1.i: $(tor_SOURCES) test_data.c test.c
+or_sha1.i: $(tor_SOURCES)
 	if test "@SHA1SUM@" != none; then \
-	  @SHA1SUM@ $(tor_SOURCES) test_data.c test.c | @SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > or_sha1.i; \
+	  @SHA1SUM@ $(tor_SOURCES) | @SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > or_sha1.i; \
 	elif test "@OPENSSL@" != none; then \
-	  @OPENSSL@ sha1 $(tor_SOURCES) test_data.c test.c | @SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2  \1\\n"/p' > or_sha1.i; \
+	  @OPENSSL@ sha1 $(tor_SOURCES) | @SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2  \1\\n"/p' > or_sha1.i; \
 	else \
 	  rm or_sha1.i; \
 	  touch or_sha1.i; \
 	fi
 
-
-
 #Dummy target to ensure that micro-revision.i _always_ gets built.
 FORCE:

Modified: tor/trunk/src/or/buffers.c
===================================================================
--- tor/trunk/src/or/buffers.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/buffers.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -1611,6 +1611,132 @@
   }
 }
 
+/** Inspect a reply from SOCKS server stored in <b>buf</b> according
+ * to <b>state</b>, removing the protocol data upon success. Return 0 on
+ * incomplete response, 1 on success and -1 on error, in which case
+ * <b>reason</b> is set to a descriptive message (free() when finished
+ * with it).
+ *
+ * As a special case, 2 is returned when user/pass is required
+ * during SOCKS5 handshake and user/pass is configured.
+ */
+int
+fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
+{
+  unsigned char *data;
+  size_t addrlen;
+
+  if (buf->datalen < 2)
+    return 0;
+
+  buf_pullup(buf, 128, 0);
+  tor_assert(buf->head && buf->head->datalen >= 2);
+
+  data = (unsigned char *) buf->head->data;
+
+  switch (state) {
+    case PROXY_SOCKS4_WANT_CONNECT_OK:
+      /* Wait for the complete response */
+      if (buf->head->datalen < 8)
+        return 0;
+
+      if (data[1] != 0x5a) {
+        *reason = tor_strdup(socks4_response_code_to_string(data[1]));
+        return -1;
+      }
+
+      /* Success */
+      buf_remove_from_front(buf, 8);
+      return 1;
+
+    case PROXY_SOCKS5_WANT_AUTH_METHOD_NONE:
+      /* we don't have any credentials */
+      if (data[1] != 0x00) {
+        *reason = tor_strdup("server doesn't support any of our "
+                             "available authentication methods");
+        return -1;
+      }
+
+      log_info(LD_NET, "SOCKS 5 client: continuing without authentication");
+      buf_clear(buf);
+      return 1;
+
+    case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929:
+      /* we have a username and password. return 1 if we can proceed without
+       * providing authentication, or 2 otherwise. */
+      switch (data[1]) {
+        case 0x00:
+          log_info(LD_NET, "SOCKS 5 client: we have auth details but server "
+                            "doesn't require authentication.");
+          buf_clear(buf);
+          return 1;
+        case 0x02:
+          log_info(LD_NET, "SOCKS 5 client: need authentication.");
+          buf_clear(buf);
+          return 2;
+        /* fall through */
+      }
+
+      *reason = tor_strdup("server doesn't support any of our available "
+                           "authentication methods");
+      return -1;
+
+    case PROXY_SOCKS5_WANT_AUTH_RFC1929_OK:
+      /* handle server reply to rfc1929 authentication */
+      if (data[1] != 0x00) {
+        *reason = tor_strdup("authentication failed");
+        return -1;
+      }
+
+      log_info(LD_NET, "SOCKS 5 client: authentication successful.");
+      buf_clear(buf);
+      return 1;
+
+    case PROXY_SOCKS5_WANT_CONNECT_OK:
+      /* response is variable length. BND.ADDR, etc, isn't needed
+       * (don't bother with buf_pullup()), but make sure to eat all
+       * the data used */
+
+      /* wait for address type field to arrive */
+      if (buf->datalen < 4)
+        return 0;
+
+      switch (data[3]) {
+        case 0x01: /* ip4 */
+          addrlen = 4;
+          break;
+        case 0x04: /* ip6 */
+          addrlen = 16;
+          break;
+        case 0x03: /* fqdn (can this happen here?) */
+          if (buf->datalen < 5)
+            return 0;
+          addrlen = 1 + data[4];
+          break;
+        default:
+          *reason = tor_strdup("invalid response to connect request");
+          return -1;
+      }
+
+      /* wait for address and port */
+      if (buf->datalen < 6 + addrlen)
+        return 0;
+
+      if (data[1] != 0x00) {
+        *reason = tor_strdup(socks5_response_code_to_string(data[1]));
+        return -1;
+      }
+
+      buf_remove_from_front(buf, 6 + addrlen);
+      return 1;
+  }
+
+  /* shouldn't get here... */
+  tor_assert(0);
+
+  return -1;
+}
+
 /** Return 1 iff buf looks more like it has an (obsolete) v0 controller
  * command on it than any valid v1 controller command. */
 int

Modified: tor/trunk/src/or/circuitbuild.c
===================================================================
--- tor/trunk/src/or/circuitbuild.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/circuitbuild.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -9,9 +9,49 @@
  * \brief The actual details of building circuits.
  **/
 
+#define CIRCUIT_PRIVATE
+
 #include "or.h"
+#include "crypto.h"
 
+/*
+ * This madness is needed because if we simply #undef log
+ * before including or.h or log.h, we get linker collisions
+ * and random segfaults due to memory corruption (and
+ * not even at calls to log() either!)
+ */
+ /* XXX022 somebody should rename Tor's log() function, so we can
+  * remove this wart. -RD */
+#undef log
+
+/*
+ * Linux doesn't provide lround in math.h by default, but mac os does...
+ * It's best just to leave math.h out of the picture entirely.
+ */
+//#define log math_h_log
+//#include <math.h>
+//#undef log
+long int lround(double x);
+double ln(double x);
+double log(double x);
+double pow(double x, double y);
+
+double
+ln(double x)
+{
+  return log(x);
+}
+
+#define log _log
+
 /********* START VARIABLES **********/
+/** Global list of circuit build times */
+// FIXME: Add this as a member for entry_guard_t instead of global?
+// Then we could do per-guard statistics, as guards are likely to
+// vary in their own latency. The downside of this is that guards
+// can change frequently, so we'd be building a lot more circuits
+// most likely.
+circuit_build_times_t circ_times;
 
 /** A global list of all circuits at this hop. */
 extern circuit_t *global_circuitlist;
@@ -47,6 +87,10 @@
  * and those changes need to be flushed to disk. */
 static int entry_guards_dirty = 0;
 
+/** If set, we're running the unit tests: we should avoid clobbering
+ * our state file or accessing get_options() or get_or_state() */
+static int unit_tests = 0;
+
 /********* END VARIABLES ************/
 
 static int circuit_deliver_create_cell(circuit_t *circ,
@@ -60,6 +104,798 @@
 static void entry_guards_changed(void);
 static time_t start_of_month(time_t when);
 
+/** Make a note that we're running unit tests (rather than running Tor
+ * itself), so we avoid clobbering our state file. */
+void
+circuitbuild_running_unit_tests(void)
+{
+  unit_tests = 1;
+}
+
+/**
+ * Return the initial default or configured timeout in milliseconds
+ */
+static double
+circuit_build_times_get_initial_timeout(void)
+{
+  double timeout;
+  if (!unit_tests && get_options()->CircuitBuildTimeout) {
+    timeout = get_options()->CircuitBuildTimeout*1000;
+    if (timeout < BUILD_TIMEOUT_MIN_VALUE) {
+      log_warn(LD_CIRC, "Config CircuitBuildTimeout too low. Setting to %ds",
+               BUILD_TIMEOUT_MIN_VALUE/1000);
+      timeout = BUILD_TIMEOUT_MIN_VALUE;
+    }
+  } else {
+    timeout = BUILD_TIMEOUT_INITIAL_VALUE;
+  }
+  return timeout;
+}
+
+/**
+ * Reset the build time state.
+ *
+ * Leave estimated parameters, timeout and network liveness intact
+ * for future use.
+ */
+void
+circuit_build_times_reset(circuit_build_times_t *cbt)
+{
+  memset(cbt->circuit_build_times, 0, sizeof(cbt->circuit_build_times));
+  cbt->pre_timeouts = 0;
+  cbt->total_build_times = 0;
+  cbt->build_times_idx = 0;
+  cbt->have_computed_timeout = 0;
+}
+
+/**
+ * Initialize the buildtimes structure for first use.
+ *
+ * Sets the initial timeout value based to either the
+ * config setting or BUILD_TIMEOUT_INITIAL_VALUE.
+ */
+void
+circuit_build_times_init(circuit_build_times_t *cbt)
+{
+  memset(cbt, 0, sizeof(*cbt));
+  cbt->timeout_ms = circuit_build_times_get_initial_timeout();
+}
+
+/**
+ * Rewind our timeout history by n positions.
+ */
+static void
+circuit_build_times_rewind_history(circuit_build_times_t *cbt, int n)
+{
+  int i = 0;
+
+  if (cbt->pre_timeouts) {
+    if (cbt->pre_timeouts > n) {
+      cbt->pre_timeouts -= n;
+    } else {
+      cbt->pre_timeouts = 0;
+    }
+    log_info(LD_CIRC,
+             "Rewound history by %d places. Current index: %d. Total: %d. "
+             "Pre-timeouts: %d", n, cbt->build_times_idx,
+             cbt->total_build_times, cbt->pre_timeouts);
+
+    tor_assert(cbt->build_times_idx == 0);
+    tor_assert(cbt->total_build_times == 0);
+    return;
+  }
+
+  cbt->build_times_idx -= n;
+  cbt->build_times_idx %= NCIRCUITS_TO_OBSERVE;
+
+  for (i = 0; i < n; i++) {
+    cbt->circuit_build_times[(i+cbt->build_times_idx)%NCIRCUITS_TO_OBSERVE]=0;
+  }
+
+  if (cbt->total_build_times > n) {
+    cbt->total_build_times -= n;
+  } else {
+    cbt->total_build_times = 0;
+  }
+
+  log_info(LD_CIRC,
+          "Rewound history by %d places. Current index: %d. "
+          "Total: %d", n, cbt->build_times_idx, cbt->total_build_times);
+}
+
+/**
+ * Add a timeoutout value to the set of build times. Time units
+ * are milliseconds
+ *
+ * circuit_build_times is a circular array, so loop around when
+ * array is full.
+ */
+int
+circuit_build_times_add_time(circuit_build_times_t *cbt, build_time_t time)
+{
+  if (time > BUILD_TIME_MAX) {
+    log_notice(LD_CIRC,
+       "Circuit build time of %ums exceeds max. Capping at 65536ms", time);
+    time = BUILD_TIME_MAX;
+  } else if (time <= 0) {
+    log_err(LD_CIRC, "Circuit build time is %u!", time);
+    return -1;
+  }
+
+  // XXX: Probably want to demote this to debug for the release.
+  log_info(LD_CIRC, "Adding circuit build time %u", time);
+
+  cbt->circuit_build_times[cbt->build_times_idx] = time;
+  cbt->build_times_idx = (cbt->build_times_idx + 1) % NCIRCUITS_TO_OBSERVE;
+  if (cbt->total_build_times < NCIRCUITS_TO_OBSERVE)
+    cbt->total_build_times++;
+
+  if ((cbt->total_build_times % BUILD_TIMES_SAVE_STATE_EVERY) == 0) {
+    /* Save state every n circuit builds */
+    if (!unit_tests && !get_options()->AvoidDiskWrites)
+      or_state_mark_dirty(get_or_state(), 0);
+  }
+
+  return 0;
+}
+
+/**
+ * Return maximum circuit build time
+ */
+static build_time_t
+circuit_build_times_max(circuit_build_times_t *cbt)
+{
+  int i = 0;
+  build_time_t max_build_time = 0;
+  for (i = 0; i < NCIRCUITS_TO_OBSERVE; i++) {
+    if (cbt->circuit_build_times[i] > max_build_time)
+      max_build_time = cbt->circuit_build_times[i];
+  }
+  return max_build_time;
+}
+
+#if 0
+/** Return minimum circuit build time */
+build_time_t
+circuit_build_times_min(circuit_build_times_t *cbt)
+{
+  int i = 0;
+  build_time_t min_build_time = BUILD_TIME_MAX;
+  for (i = 0; i < NCIRCUITS_TO_OBSERVE; i++) {
+    if (cbt->circuit_build_times[i] && /* 0 <-> uninitialized */
+        cbt->circuit_build_times[i] < min_build_time)
+      min_build_time = cbt->circuit_build_times[i];
+  }
+  if (min_build_time == BUILD_TIME_MAX) {
+    log_warn(LD_CIRC, "No build times less than BUILD_TIME_MAX!");
+  }
+  return min_build_time;
+}
+#endif
+
+/**
+ * Calculate and return a histogram for the set of build times.
+ *
+ * Returns an allocated array of histrogram bins representing
+ * the frequency of index*BUILDTIME_BIN_WIDTH millisecond
+ * build times. Also outputs the number of bins in nbins.
+ *
+ * The return value must be freed by the caller.
+ */
+static uint32_t *
+circuit_build_times_create_histogram(circuit_build_times_t *cbt,
+                                     build_time_t *nbins)
+{
+  uint32_t *histogram;
+  build_time_t max_build_time = circuit_build_times_max(cbt);
+  int i, c;
+
+  *nbins = 1 + (max_build_time / BUILDTIME_BIN_WIDTH);
+  histogram = tor_malloc_zero(*nbins * sizeof(build_time_t));
+
+  // calculate histogram
+  for (i = 0; i < NCIRCUITS_TO_OBSERVE; i++) {
+    if (cbt->circuit_build_times[i] == 0) continue; /* 0 <-> uninitialized */
+
+    c = (cbt->circuit_build_times[i] / BUILDTIME_BIN_WIDTH);
+    histogram[c]++;
+  }
+
+  return histogram;
+}
+
+/**
+ * Return the most frequent build time (rounded to BUILDTIME_BIN_WIDTH ms).
+ *
+ * Ties go in favor of the slower time.
+ */
+static build_time_t
+circuit_build_times_mode(circuit_build_times_t *cbt)
+{
+  build_time_t i, nbins, max_bin=0;
+  uint32_t *histogram = circuit_build_times_create_histogram(cbt, &nbins);
+
+  for (i = 0; i < nbins; i++) {
+    if (histogram[i] >= histogram[max_bin]) {
+      max_bin = i;
+    }
+  }
+
+  tor_free(histogram);
+
+  return max_bin*BUILDTIME_BIN_WIDTH+BUILDTIME_BIN_WIDTH/2;
+}
+
+/**
+ * Output a histogram of current circuit build times to
+ * the or_state_t state structure.
+ */
+void
+circuit_build_times_update_state(circuit_build_times_t *cbt,
+                                 or_state_t *state)
+{
+  uint32_t *histogram;
+  build_time_t i = 0;
+  build_time_t nbins = 0;
+  config_line_t **next, *line;
+
+  histogram = circuit_build_times_create_histogram(cbt, &nbins);
+  // write to state
+  config_free_lines(state->BuildtimeHistogram);
+  next = &state->BuildtimeHistogram;
+  *next = NULL;
+
+  state->TotalBuildTimes = cbt->total_build_times;
+
+  for (i = 0; i < nbins; i++) {
+    // compress the histogram by skipping the blanks
+    if (histogram[i] == 0) continue;
+    *next = line = tor_malloc_zero(sizeof(config_line_t));
+    line->key = tor_strdup("CircuitBuildTimeBin");
+    line->value = tor_malloc(25);
+    tor_snprintf(line->value, 25, "%d %d",
+            i*BUILDTIME_BIN_WIDTH+BUILDTIME_BIN_WIDTH/2, histogram[i]);
+    next = &(line->next);
+  }
+
+  if (!unit_tests) {
+    if (!get_options()->AvoidDiskWrites)
+      or_state_mark_dirty(get_or_state(), 0);
+  }
+
+  if (histogram) tor_free(histogram);
+}
+
+/**
+ * Shuffle the build times array.
+ *
+ * Stolen from http://en.wikipedia.org/wiki/Fisher\u2013Yates_shuffle
+ */
+static void
+circuit_build_times_shuffle_array(circuit_build_times_t *cbt)
+{
+   int n = cbt->total_build_times;
+
+   /* This code can only be run on a compact array */
+   tor_assert(cbt->total_build_times == cbt->build_times_idx);
+   while (n-- > 1) {
+     int k = crypto_rand_int(n + 1); /* 0 <= k <= n. */
+     build_time_t tmp = cbt->circuit_build_times[k];
+     cbt->circuit_build_times[k] = cbt->circuit_build_times[n];
+     cbt->circuit_build_times[n] = tmp;
+   }
+}
+
+/**
+ * Load histogram from <b>state</b>, shuffling the resulting array
+ * after we do so. Use this result to estimate parameters and
+ * calculate the timeout.
+ *
+ * Returns -1 and sets msg on error. Msg must be freed by the caller.
+ */
+int
+circuit_build_times_parse_state(circuit_build_times_t *cbt,
+                                or_state_t *state, char **msg)
+{
+  int tot_values = 0, N = 0;
+  config_line_t *line;
+  int i;
+  *msg = NULL;
+  circuit_build_times_init(cbt);
+
+  /* We don't support decreasing the table size yet */
+  tor_assert(state->TotalBuildTimes <= NCIRCUITS_TO_OBSERVE);
+
+  for (line = state->BuildtimeHistogram; line; line = line->next) {
+    smartlist_t *args = smartlist_create();
+    smartlist_split_string(args, line->value, " ",
+                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+    if (smartlist_len(args) < 2) {
+      *msg = tor_strdup("Unable to parse circuit build times: "
+                        "Too few arguments to CircuitBuildTime");
+      SMARTLIST_FOREACH(args, char*, cp, tor_free(cp));
+      smartlist_free(args);
+      break;
+    } else {
+      const char *ms_str = smartlist_get(args,0);
+      const char *count_str = smartlist_get(args,1);
+      uint32_t count, k;
+      build_time_t ms;
+      int ok;
+      ms = (build_time_t)tor_parse_ulong(ms_str, 0, 0,
+                                         BUILD_TIME_MAX, &ok, NULL);
+      if (!ok) {
+        *msg = tor_strdup("Unable to parse circuit build times: "
+                          "Unparsable bin number");
+        break;
+      }
+      count = (uint32_t)tor_parse_ulong(count_str, 0, 0,
+                                        UINT32_MAX, &ok, NULL);
+      if (!ok) {
+        *msg = tor_strdup("Unable to parse circuit build times: "
+                          "Unparsable bin count");
+        break;
+      }
+
+      for (k = 0; k < count; k++) {
+        circuit_build_times_add_time(cbt, ms);
+      }
+      N++;
+      SMARTLIST_FOREACH(args, char*, cp, tor_free(cp));
+      smartlist_free(args);
+    }
+
+  }
+
+  circuit_build_times_shuffle_array(cbt);
+
+  /* Verify that we didn't overwrite any indexes */
+  for (i=0; i < NCIRCUITS_TO_OBSERVE; i++) {
+    if (!cbt->circuit_build_times[i])
+      break;
+    tot_values++;
+  }
+  log_info(LD_CIRC,
+           "Loaded %d/%d values from %d lines in circuit time histogram",
+           tot_values, cbt->total_build_times, N);
+  tor_assert(cbt->total_build_times == state->TotalBuildTimes);
+  tor_assert(tot_values == cbt->total_build_times);
+  circuit_build_times_set_timeout(cbt);
+  return *msg ? -1 : 0;
+}
+
+/**
+ * Estimates the Xm and Alpha parameters using
+ * http://en.wikipedia.org/wiki/Pareto_distribution#Parameter_estimation
+ *
+ * The notable difference is that we use mode instead of min to estimate Xm.
+ * This is because our distribution is frechet-like. We claim this is
+ * an acceptable approximation because we are only concerned with the
+ * accuracy of the CDF of the tail.
+ */
+void
+circuit_build_times_update_alpha(circuit_build_times_t *cbt)
+{
+  build_time_t *x=cbt->circuit_build_times;
+  double a = 0;
+  int n=0,i=0;
+
+  /* http://en.wikipedia.org/wiki/Pareto_distribution#Parameter_estimation */
+  /* We sort of cheat here and make our samples slightly more pareto-like
+   * and less frechet-like. */
+  cbt->Xm = circuit_build_times_mode(cbt);
+
+  for (i=0; i< NCIRCUITS_TO_OBSERVE; i++) {
+    if (!x[i]) {
+      continue;
+    }
+
+    if (x[i] < cbt->Xm) {
+      a += ln(cbt->Xm);
+    } else {
+      a += ln(x[i]);
+    }
+    n++;
+  }
+
+  if (n!=cbt->total_build_times) {
+    log_err(LD_CIRC, "Discrepancy in build times count: %d vs %d", n,
+            cbt->total_build_times);
+  }
+  tor_assert(n==cbt->total_build_times);
+
+  a -= n*ln(cbt->Xm);
+  a = n/a;
+
+  cbt->alpha = a;
+}
+
+/**
+ * This is the Pareto Quantile Function. It calculates the point x
+ * in the distribution such that F(x) = quantile (ie quantile*100%
+ * of the mass of the density function is below x on the curve).
+ *
+ * We use it to calculate the timeout and also to generate synthetic
+ * values of time for circuits that timeout before completion.
+ *
+ * See http://en.wikipedia.org/wiki/Quantile_function,
+ * http://en.wikipedia.org/wiki/Inverse_transform_sampling and
+ * http://en.wikipedia.org/wiki/Pareto_distribution#Generating_a_
+ *     random_sample_from_Pareto_distribution
+ * That's right. I'll cite wikipedia all day long.
+ *
+ * Return value is in milliseconds.
+ */
+double
+circuit_build_times_calculate_timeout(circuit_build_times_t *cbt,
+                                      double quantile)
+{
+  double ret;
+  tor_assert(quantile >= 0);
+  tor_assert(1.0-quantile > 0);
+  tor_assert(cbt->Xm > 0);
+
+  ret = cbt->Xm/pow(1.0-quantile,1.0/cbt->alpha);
+  if (ret > INT32_MAX) {
+    ret = INT32_MAX;
+  }
+  tor_assert(ret > 0);
+  return ret;
+}
+
+/** Pareto CDF */
+double
+circuit_build_times_cdf(circuit_build_times_t *cbt, double x)
+{
+  double ret;
+  tor_assert(cbt->Xm > 0);
+  ret = 1.0-pow(cbt->Xm/x,cbt->alpha);
+  tor_assert(0 <= ret && ret <= 1.0);
+  return ret;
+}
+
+/**
+ * Generate a synthetic time using our distribution parameters.
+ *
+ * The return value will be within the [q_lo, q_hi) quantile points
+ * on the CDF.
+ */
+build_time_t
+circuit_build_times_generate_sample(circuit_build_times_t *cbt,
+                                    double q_lo, double q_hi)
+{
+  uint64_t r = crypto_rand_uint64(UINT64_MAX-1);
+  build_time_t ret;
+  double u;
+
+  /* Generate between [q_lo, q_hi) */
+  q_hi -= 1.0/(INT32_MAX);
+
+  tor_assert(q_lo >= 0);
+  tor_assert(q_hi < 1);
+  tor_assert(q_lo < q_hi);
+
+  u = q_lo + ((q_hi-q_lo)*r)/(1.0*UINT64_MAX);
+
+  tor_assert(0 <= u && u < 1.0);
+  /* circuit_build_times_calculate_timeout returns <= INT32_MAX */
+  ret = (build_time_t)lround(circuit_build_times_calculate_timeout(cbt, u));
+  tor_assert(ret > 0);
+  return ret;
+}
+
+/** Generate points in [cutoff, 1.0) on the CDF. */
+void
+circuit_build_times_add_timeout_worker(circuit_build_times_t *cbt,
+                                       double quantile_cutoff)
+{
+  build_time_t gentime = circuit_build_times_generate_sample(cbt,
+              quantile_cutoff, MAX_SYNTHETIC_QUANTILE);
+
+  if (gentime < (build_time_t)lround(cbt->timeout_ms)) {
+    log_warn(LD_CIRC,
+             "Generated a synthetic timeout LESS than the current timeout: "
+             "%ums vs %lfms using Xm: %d a: %lf, q: %lf",
+             gentime, cbt->timeout_ms, cbt->Xm, cbt->alpha, quantile_cutoff);
+  } else if (gentime > BUILD_TIME_MAX) {
+    gentime = BUILD_TIME_MAX;
+    log_info(LD_CIRC,
+             "Generated a synthetic timeout larger than the max: %u",
+             gentime);
+  } else {
+    log_info(LD_CIRC, "Generated synthetic circuit build time %u for timeout",
+            gentime);
+  }
+
+  circuit_build_times_add_time(cbt, gentime);
+}
+
+/**
+ * Estimate an initial alpha parameter by solving the quantile
+ * function with a quantile point and a specific timeout value.
+ */
+void
+circuit_build_times_initial_alpha(circuit_build_times_t *cbt,
+                                  double quantile, double timeout_ms)
+{
+  // Q(u) = Xm/((1-u)^(1/a))
+  // Q(0.8) = Xm/((1-0.8))^(1/a)) = CircBuildTimeout
+  // CircBuildTimeout = Xm/((1-0.8))^(1/a))
+  // CircBuildTimeout = Xm*((1-0.8))^(-1/a))
+  // ln(CircBuildTimeout) = ln(Xm)+ln(((1-0.8)))*(-1/a)
+  // -ln(1-0.8)/(ln(CircBuildTimeout)-ln(Xm))=a
+  tor_assert(quantile > 0);
+  tor_assert(cbt->Xm > 0);
+  cbt->alpha = ln(1.0-quantile)/(ln(cbt->Xm)-ln(timeout_ms));
+  tor_assert(cbt->alpha > 0);
+}
+
+/**
+ * Generate synthetic timeout values for the timeouts
+ * that have happened before we estimated our parameters.
+ */
+static void
+circuit_build_times_count_pretimeouts(circuit_build_times_t *cbt)
+{
+  /* Store a timeout as a random position past the current
+   * cutoff on the pareto curve */
+  if (cbt->pre_timeouts) {
+    double timeout_quantile = 1.0-
+          ((double)cbt->pre_timeouts)/
+                    (cbt->pre_timeouts+cbt->total_build_times);
+    /* Make sure it doesn't exceed the synthetic max */
+    timeout_quantile *= MAX_SYNTHETIC_QUANTILE;
+    cbt->Xm = circuit_build_times_mode(cbt);
+    tor_assert(cbt->Xm > 0);
+    /* Use current timeout to get an estimate on alpha */
+    circuit_build_times_initial_alpha(cbt, timeout_quantile,
+                                      cbt->timeout_ms);
+    while (cbt->pre_timeouts-- != 0) {
+      circuit_build_times_add_timeout_worker(cbt, timeout_quantile);
+    }
+    cbt->pre_timeouts = 0;
+  }
+}
+
+/**
+ * Returns true if we need circuits to be built
+ */
+int
+circuit_build_times_needs_circuits(circuit_build_times_t *cbt)
+{
+  /* Return true if < MIN_CIRCUITS_TO_OBSERVE */
+  if (cbt->total_build_times < MIN_CIRCUITS_TO_OBSERVE)
+    return 1;
+  return 0;
+}
+
+/**
+ * Returns true if we should build a timeout test circuit
+ * right now.
+ */
+int
+circuit_build_times_needs_circuits_now(circuit_build_times_t *cbt)
+{
+  return circuit_build_times_needs_circuits(cbt) &&
+    approx_time()-cbt->last_circ_at > BUILD_TIMES_TEST_FREQUENCY;
+}
+
+/**
+ * Called to indicate that the network showed some signs of liveness.
+ */
+void
+circuit_build_times_network_is_live(circuit_build_times_t *cbt)
+{
+  cbt->liveness.network_last_live = approx_time();
+  cbt->liveness.nonlive_discarded = 0;
+  cbt->liveness.nonlive_timeouts = 0;
+}
+
+/**
+ * Called to indicate that we completed a circuit. Because this circuit
+ * succeeded, it doesn't count as a timeout-after-the-first-hop.
+ */
+void
+circuit_build_times_network_circ_success(circuit_build_times_t *cbt)
+{
+  cbt->liveness.timeouts_after_firsthop[cbt->liveness.after_firsthop_idx] = 0;
+  cbt->liveness.after_firsthop_idx++;
+  cbt->liveness.after_firsthop_idx %= RECENT_CIRCUITS;
+}
+
+/**
+ * A circuit just timed out. If there has been no recent network activity
+ * at all, but this circuit was launched back when we thought the network
+ * was live, increment the number of "nonlive" circuit timeouts.
+ *
+ * Also distinguish between whether it failed before the first hop
+ * and record that in our history for later deciding if the network has
+ * changed.
+ */
+static void
+circuit_build_times_network_timeout(circuit_build_times_t *cbt,
+                                    int did_onehop, time_t start_time)
+{
+  time_t now = time(NULL);
+  /*
+   * Check if this is a timeout that was for a circuit that spent its
+   * entire existence during a time where we have had no network activity.
+   *
+   * Also double check that it is a valid timeout after we have possibly
+   * just recently reset cbt->timeout_ms.
+   */
+  if (cbt->liveness.network_last_live <= start_time &&
+          start_time <= (now - cbt->timeout_ms/1000.0)) {
+    cbt->liveness.nonlive_timeouts++;
+  }
+
+  /* Check for one-hop timeout */
+  if (did_onehop) {
+    cbt->liveness.timeouts_after_firsthop[cbt->liveness.after_firsthop_idx]=1;
+    cbt->liveness.after_firsthop_idx++;
+    cbt->liveness.after_firsthop_idx %= RECENT_CIRCUITS;
+  }
+}
+
+/**
+ * Returns false if the network has not received a cell or tls handshake
+ * in the past NETWORK_NOTLIVE_TIMEOUT_COUNT circuits.
+ *
+ * Also has the side effect of rewinding the circuit time history
+ * in the case of recent liveness changes.
+ */
+int
+circuit_build_times_network_check_live(circuit_build_times_t *cbt)
+{
+  time_t now = approx_time();
+  if (cbt->liveness.nonlive_timeouts >= NETWORK_NONLIVE_DISCARD_COUNT) {
+    if (!cbt->liveness.nonlive_discarded) {
+      cbt->liveness.nonlive_discarded = 1;
+      log_notice(LD_CIRC, "Network is no longer live (too many recent "
+                "circuit timeouts). Dead for %ld seconds.",
+                (long int)(now - cbt->liveness.network_last_live));
+      /* Only discard NETWORK_NONLIVE_TIMEOUT_COUNT-1 because we stopped
+       * counting after that */
+      circuit_build_times_rewind_history(cbt, NETWORK_NONLIVE_TIMEOUT_COUNT-1);
+    }
+    return 0;
+  } else if (cbt->liveness.nonlive_timeouts >= NETWORK_NONLIVE_TIMEOUT_COUNT) {
+    if (cbt->timeout_ms < circuit_build_times_get_initial_timeout()) {
+      log_notice(LD_CIRC,
+                "Network is flaky. No activity for %ld seconds. "
+                "Temporarily raising timeout to %lds.",
+                (long int)(now - cbt->liveness.network_last_live),
+                lround(circuit_build_times_get_initial_timeout()/1000));
+      cbt->timeout_ms = circuit_build_times_get_initial_timeout();
+    }
+
+    return 0;
+  }
+
+  return 1;
+}
+
+/**
+ * Returns true if we have seen more than MAX_RECENT_TIMEOUT_COUNT of
+ * the past RECENT_CIRCUITS time out after the first hop. Used to detect
+ * if the network connection has changed significantly.
+ *
+ * Also resets the entire timeout history in this case and causes us
+ * to restart the process of building test circuits and estimating a
+ * new timeout.
+ */
+int
+circuit_build_times_network_check_changed(circuit_build_times_t *cbt)
+{
+  int total_build_times = cbt->total_build_times;
+  int timeout_count=0;
+  int i;
+
+  /* how many of our recent circuits made it to the first hop but then
+   * timed out? */
+  for (i = 0; i < RECENT_CIRCUITS; i++) {
+    timeout_count += cbt->liveness.timeouts_after_firsthop[i];
+  }
+
+  /* If 75% of our recent circuits are timing out after the first hop,
+   * we need to re-estimate a new initial alpha and timeout. */
+  if (timeout_count < MAX_RECENT_TIMEOUT_COUNT) {
+    return 0;
+  }
+
+  circuit_build_times_reset(cbt);
+  memset(cbt->liveness.timeouts_after_firsthop, 0,
+          sizeof(cbt->liveness.timeouts_after_firsthop));
+  cbt->liveness.after_firsthop_idx = 0;
+
+  /* Check to see if this has happened before. If so, double the timeout
+   * to give people on abysmally bad network connections a shot at access */
+  if (cbt->timeout_ms >= circuit_build_times_get_initial_timeout()) {
+    cbt->timeout_ms *= 2;
+  } else {
+    cbt->timeout_ms = circuit_build_times_get_initial_timeout();
+  }
+
+  log_notice(LD_CIRC,
+            "Network connection speed appears to have changed. Resetting "
+            "timeout to %lds after %d timeouts and %d buildtimes.",
+            lround(cbt->timeout_ms/1000), timeout_count, total_build_times);
+
+  return 1;
+}
+
+/**
+ * Store a timeout as a synthetic value.
+ *
+ * Returns true if the store was successful and we should possibly
+ * update our timeout estimate.
+ */
+int
+circuit_build_times_add_timeout(circuit_build_times_t *cbt,
+                                int did_onehop,
+                                time_t start_time)
+{
+  circuit_build_times_network_timeout(cbt, did_onehop, start_time);
+
+  /* Only count timeouts if network is live.. */
+  if (!circuit_build_times_network_check_live(cbt)) {
+    return 0;
+  }
+
+  /* If there are a ton of timeouts, we should reduce
+   * the circuit build timeout */
+  if (circuit_build_times_network_check_changed(cbt)) {
+    return 0;
+  }
+
+  if (!cbt->have_computed_timeout) {
+    /* Store a timeout before we have enough data */
+    cbt->pre_timeouts++;
+    log_info(LD_CIRC,
+             "Not enough circuits yet to calculate a new build timeout."
+             " Need %d more.",
+             MIN_CIRCUITS_TO_OBSERVE-cbt->total_build_times);
+    return 0;
+  }
+
+  circuit_build_times_count_pretimeouts(cbt);
+  circuit_build_times_add_timeout_worker(cbt, BUILDTIMEOUT_QUANTILE_CUTOFF);
+
+  return 1;
+}
+
+/**
+ * Estimate a new timeout based on history and set our timeout
+ * variable accordingly.
+ */
+void
+circuit_build_times_set_timeout(circuit_build_times_t *cbt)
+{
+  if (cbt->total_build_times < MIN_CIRCUITS_TO_OBSERVE) {
+    return;
+  }
+
+  circuit_build_times_count_pretimeouts(cbt);
+  circuit_build_times_update_alpha(cbt);
+
+  cbt->timeout_ms = circuit_build_times_calculate_timeout(cbt,
+                                BUILDTIMEOUT_QUANTILE_CUTOFF);
+
+  cbt->have_computed_timeout = 1;
+
+  if (cbt->timeout_ms < BUILD_TIMEOUT_MIN_VALUE) {
+    log_warn(LD_CIRC, "Set buildtimeout to low value %lfms. Setting to %dms",
+             cbt->timeout_ms, BUILD_TIMEOUT_MIN_VALUE);
+    cbt->timeout_ms = BUILD_TIMEOUT_MIN_VALUE;
+  }
+
+  log_info(LD_CIRC,
+           "Set circuit build timeout to %lds (%lfms, Xm: %d, a: %lf) "
+           "based on %d circuit times", lround(cbt->timeout_ms/1000),
+           cbt->timeout_ms, cbt->Xm, cbt->alpha, cbt->total_build_times);
+
+}
+
 /** Iterate over values of circ_id, starting from conn-\>next_circ_id,
  * and with the high bit specified by conn-\>circ_id_type, until we get
  * a circ_id that is not in use by any other circuit on that conn.
@@ -527,9 +1363,16 @@
   routerinfo_t *me = router_get_my_routerinfo();
   if (!me)
     return 0;
-  if (me->dir_port)
+  control_event_server_status(LOG_NOTICE,
+                              "CHECKING_REACHABILITY ORADDRESS=%s:%d",
+                              me->address, me->or_port);
+  if (me->dir_port) {
     tor_snprintf(dirbuf, sizeof(dirbuf), " and DirPort %s:%d",
                  me->address, me->dir_port);
+    control_event_server_status(LOG_NOTICE,
+                                "CHECKING_REACHABILITY DIRADDRESS=%s:%d",
+                                me->address, me->dir_port);
+  }
   log(LOG_NOTICE, LD_OR, "Now checking whether ORPort %s:%d%s %s reachable... "
                          "(this may take up to %d minutes -- look for log "
                          "messages indicating success)",
@@ -537,6 +1380,7 @@
       me->dir_port ? dirbuf : "",
       me->dir_port ? "are" : "is",
       TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT/60);
+
   return 1;
 }
 
@@ -633,8 +1477,17 @@
     log_debug(LD_CIRC,"starting to send subsequent skin.");
     hop = onion_next_hop_in_cpath(circ->cpath);
     if (!hop) {
+      struct timeval end;
+      long timediff;
+      tor_gettimeofday(&end);
+      timediff = tv_mdiff(&circ->_base.highres_created, &end);
+      if (timediff > INT32_MAX)
+        timediff = INT32_MAX;
       /* done building the circuit. whew. */
       circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN);
+      circuit_build_times_add_time(&circ_times, (build_time_t)timediff);
+      circuit_build_times_network_circ_success(&circ_times);
+      circuit_build_times_set_timeout(&circ_times);
       log_info(LD_CIRC,"circuit built!");
       circuit_reset_failure_count(0);
       if (circ->build_state->onehop_tunnel)
@@ -1436,14 +2289,17 @@
 /** Log a warning if the user specified an exit for the circuit that
  * has been excluded from use by ExcludeNodes or ExcludeExitNodes. */
 static void
-warn_if_last_router_excluded(uint8_t purpose, const extend_info_t *exit)
+warn_if_last_router_excluded(origin_circuit_t *circ, const extend_info_t *exit)
 {
   or_options_t *options = get_options();
   routerset_t *rs = options->ExcludeNodes;
   const char *description;
-  int severity;
   int domain = LD_CIRC;
+  uint8_t purpose = circ->_base.purpose;
 
+  if (circ->build_state->onehop_tunnel)
+    return;
+
   switch (purpose)
     {
     default:
@@ -1455,48 +2311,40 @@
                (int)purpose);
       return;
     case CIRCUIT_PURPOSE_C_GENERAL:
+      if (circ->build_state->is_internal)
+        return;
       description = "Requested exit node";
       rs = options->_ExcludeExitNodesUnion;
-      severity = LOG_WARN;
       break;
     case CIRCUIT_PURPOSE_C_INTRODUCING:
     case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
     case CIRCUIT_PURPOSE_C_INTRODUCE_ACKED:
-      description = "Introduction point for hidden service";
-      severity = LOG_INFO;
-      break;
+    case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
+    case CIRCUIT_PURPOSE_S_CONNECT_REND:
+    case CIRCUIT_PURPOSE_S_REND_JOINED:
+    case CIRCUIT_PURPOSE_TESTING:
+      return;
     case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
     case CIRCUIT_PURPOSE_C_REND_READY:
     case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
     case CIRCUIT_PURPOSE_C_REND_JOINED:
       description = "Chosen rendezvous point";
-      severity = LOG_WARN;
       domain = LD_BUG;
       break;
-    case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
-      description = "Chosen introduction point";
-      severity = LOG_INFO;
-      break;
-    case CIRCUIT_PURPOSE_S_CONNECT_REND:
-    case CIRCUIT_PURPOSE_S_REND_JOINED:
-      description = "Client-selected rendezvous point";
-      severity = LOG_INFO;
-      break;
-    case CIRCUIT_PURPOSE_TESTING:
-      description = "Target for testing circuit";
-      severity = LOG_INFO;
-      break;
     case CIRCUIT_PURPOSE_CONTROLLER:
       rs = options->_ExcludeExitNodesUnion;
       description = "Controller-selected circuit target";
-      severity = LOG_WARN;
       break;
     }
 
-  if (routerset_contains_extendinfo(rs, exit))
-    log_fn(severity, domain, "%s '%s' is in ExcludeNodes%s.  Using anyway.",
+  if (routerset_contains_extendinfo(rs, exit)) {
+    log_fn(LOG_WARN, domain, "%s '%s' is in ExcludeNodes%s. Using anyway "
+           "(circuit purpose %d).",
            description,exit->nickname,
-           rs==options->ExcludeNodes?"":" or ExcludeExitNodes.");
+           rs==options->ExcludeNodes?"":" or ExcludeExitNodes",
+           (int)purpose);
+    circuit_log_path(LOG_WARN, domain, circ);
+  }
 
   return;
 }
@@ -1521,7 +2369,7 @@
   }
 
   if (exit) { /* the circuit-builder pre-requested one */
-    warn_if_last_router_excluded(circ->_base.purpose, exit);
+    warn_if_last_router_excluded(circ, exit);
     log_info(LD_CIRC,"Using requested exit node '%s'", exit->nickname);
     exit = extend_info_dup(exit);
   } else { /* we have to decide one */
@@ -1568,6 +2416,7 @@
 circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit)
 {
   int err_reason = 0;
+  warn_if_last_router_excluded(circ, exit);
   circuit_append_new_exit(circ, exit);
   circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING);
   if ((err_reason = circuit_send_next_onion_skin(circ))<0) {
@@ -1825,7 +2674,7 @@
 
   hop->extend_info = extend_info_dup(choice);
 
-  hop->package_window = CIRCWINDOW_START;
+  hop->package_window = circuit_initial_package_window();
   hop->deliver_window = CIRCWINDOW_START;
 
   return 0;

Modified: tor/trunk/src/or/circuitlist.c
===================================================================
--- tor/trunk/src/or/circuitlist.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/circuitlist.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -361,14 +361,28 @@
   }
 }
 
+/** Pick a reasonable package_window to start out for our circuits.
+ * Originally this was hard-coded at 1000, but now the consensus votes
+ * on the answer. See proposal 168. */
+int32_t
+circuit_initial_package_window(void)
+{
+  int32_t num = networkstatus_get_param(NULL, "circwindow", CIRCWINDOW_START);
+  /* If the consensus tells us a negative number, we'd assert. */
+  if (num < 0)
+    num = CIRCWINDOW_START;
+  return num;
+}
+
 /** Initialize the common elements in a circuit_t, and add it to the global
  * list. */
 static void
 init_circuit_base(circuit_t *circ)
 {
   circ->timestamp_created = time(NULL);
+  tor_gettimeofday(&circ->highres_created);
 
-  circ->package_window = CIRCWINDOW_START;
+  circ->package_window = circuit_initial_package_window();
   circ->deliver_window = CIRCWINDOW_START;
 
   circuit_add(circ);
@@ -395,6 +409,8 @@
 
   init_circuit_base(TO_CIRCUIT(circ));
 
+  circ_times.last_circ_at = approx_time();
+
   return circ;
 }
 
@@ -447,11 +463,9 @@
       rend_data_free(ocirc->rend_data);
   } else {
     or_circuit_t *ocirc = TO_OR_CIRCUIT(circ);
-#ifdef ENABLE_BUFFER_STATS
     /* Remember cell statistics for this circuit before deallocating. */
     if (get_options()->CellStatistics)
-      add_circ_to_buffer_stats(circ, time(NULL));
-#endif
+      rep_hist_buffer_stats_add_circ(circ, time(NULL));
     mem = ocirc;
     memlen = sizeof(or_circuit_t);
     tor_assert(circ->magic == OR_CIRCUIT_MAGIC);

Modified: tor/trunk/src/or/circuituse.c
===================================================================
--- tor/trunk/src/or/circuituse.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/circuituse.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -20,6 +20,8 @@
 static void circuit_expire_old_circuits(time_t now);
 static void circuit_increment_failure_count(void);
 
+long int lround(double x);
+
 /** Return 1 if <b>circ</b> could be returned by circuit_get_best().
  * Else return 0.
  */
@@ -263,16 +265,18 @@
 void
 circuit_expire_building(time_t now)
 {
-  circuit_t *victim, *circ = global_circuitlist;
-  time_t general_cutoff = now - get_options()->CircuitBuildTimeout;
-  time_t begindir_cutoff = now - get_options()->CircuitBuildTimeout/2;
+  circuit_t *victim, *next_circ = global_circuitlist;
+  /* circ_times.timeout is BUILD_TIMEOUT_INITIAL_VALUE if we haven't
+   * decided on a customized one yet */
+  time_t general_cutoff = now - lround(circ_times.timeout_ms/1000);
+  time_t begindir_cutoff = now - lround(circ_times.timeout_ms/2000);
   time_t introcirc_cutoff = begindir_cutoff;
   cpath_build_state_t *build_state;
 
-  while (circ) {
+  while (next_circ) {
     time_t cutoff;
-    victim = circ;
-    circ = circ->next;
+    victim = next_circ;
+    next_circ = next_circ->next;
     if (!CIRCUIT_IS_ORIGIN(victim) || /* didn't originate here */
         victim->marked_for_close) /* don't mess with marked circs */
       continue;
@@ -343,6 +347,12 @@
             continue;
           break;
       }
+    } else { /* circuit not open, consider recording failure as timeout */
+      int first_hop_succeeded = TO_ORIGIN_CIRCUIT(victim)->cpath &&
+            TO_ORIGIN_CIRCUIT(victim)->cpath->state == CPATH_STATE_OPEN;
+      if (circuit_build_times_add_timeout(&circ_times, first_hop_succeeded,
+                                          victim->timestamp_created))
+        circuit_build_times_set_timeout(&circ_times);
     }
 
     if (victim->n_conn)
@@ -431,11 +441,11 @@
 }
 
 /** Don't keep more than this many unused open circuits around. */
-#define MAX_UNUSED_OPEN_CIRCUITS 12
+#define MAX_UNUSED_OPEN_CIRCUITS 14
 
 /** Figure out how many circuits we have open that are clean. Make
  * sure it's enough for all the upcoming behaviors we predict we'll have.
- * But if we have too many, close the not-so-useful ones.
+ * But put an upper bound on the total number of circuits.
  */
 static void
 circuit_predict_and_launch_new(void)
@@ -517,6 +527,19 @@
     circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
     return;
   }
+
+  /* Finally, check to see if we still need more circuits to learn
+   * a good build timeout. But if we're close to our max number we
+   * want, don't do another -- we want to leave a few slots open so
+   * we can still build circuits preemptively as needed. */
+  if (num < MAX_UNUSED_OPEN_CIRCUITS-2 &&
+      circuit_build_times_needs_circuits_now(&circ_times)) {
+    flags = CIRCLAUNCH_NEED_CAPACITY;
+    log_info(LD_CIRC,
+             "Have %d clean circs need another buildtime test circ.", num);
+    circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
+    return;
+  }
 }
 
 /** Build a new test circuit every 5 minutes */
@@ -624,6 +647,11 @@
   tor_fragile_assert();
 }
 
+/** If we haven't yet decided on a good timeout value for circuit
+ * building, we close idles circuits aggressively so we can get more
+ * data points. */
+#define IDLE_TIMEOUT_WHILE_LEARNING (10*60)
+
 /** Find each circuit that has been unused for too long, or dirty
  * for too long and has no streams on it: mark it for close.
  */
@@ -631,8 +659,16 @@
 circuit_expire_old_circuits(time_t now)
 {
   circuit_t *circ;
-  time_t cutoff = now - get_options()->CircuitIdleTimeout;
+  time_t cutoff;
 
+  if (circuit_build_times_needs_circuits(&circ_times)) {
+    /* Circuits should be shorter lived if we need more of them
+     * for learning a good build timeout */
+    cutoff = now - IDLE_TIMEOUT_WHILE_LEARNING;
+  } else {
+    cutoff = now - get_options()->CircuitIdleTimeout;
+  }
+
   for (circ = global_circuitlist; circ; circ = circ->next) {
     if (circ->marked_for_close || ! CIRCUIT_IS_ORIGIN(circ))
       continue;
@@ -724,17 +760,12 @@
 static void
 circuit_testing_failed(origin_circuit_t *circ, int at_last_hop)
 {
-  routerinfo_t *me = router_get_my_routerinfo();
   if (server_mode(get_options()) && check_whether_orport_reachable())
     return;
-  if (!me)
-    return;
 
   log_info(LD_GENERAL,
            "Our testing circuit (to see if your ORPort is reachable) "
            "has failed. I'll try again later.");
-  control_event_server_status(LOG_WARN, "REACHABILITY_FAILED ORADDRESS=%s:%d",
-                             me->address, me->or_port);
 
   /* These aren't used yet. */
   (void)circ;
@@ -811,6 +842,9 @@
                "(%s:%d). I'm going to try to rotate to a better connection.",
                n_conn->_base.address, n_conn->_base.port);
       n_conn->is_bad_for_new_circs = 1;
+    } else {
+      log_info(LD_OR,
+               "Our circuit died before the first hop with no connection");
     }
     if (n_conn_id) {
       entry_guard_register_connect_status(n_conn_id, 0, 1, time(NULL));

Modified: tor/trunk/src/or/command.c
===================================================================
--- tor/trunk/src/or/command.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/command.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -575,7 +575,7 @@
     /* Consider all the other addresses; if any matches, this connection is
      * "canonical." */
     tor_addr_t addr;
-    const char *next = decode_address_from_payload(&addr, cp, end-cp);
+    const char *next = decode_address_from_payload(&addr, cp, (int)(end-cp));
     if (next == NULL) {
       log_fn(LOG_PROTOCOL_WARN,  LD_OR,
              "Bad address in netinfo cell; closing connection.");
@@ -610,9 +610,11 @@
            conn->_base.address, (int)conn->_base.port,
            apparent_skew>0 ? "ahead" : "behind", dbuf,
            apparent_skew>0 ? "behind" : "ahead");
-    control_event_general_status(LOG_WARN,
-                        "CLOCK_SKEW SKEW=%ld SOURCE=OR:%s:%d",
-                        apparent_skew, conn->_base.address, conn->_base.port);
+    if (severity == LOG_WARN) /* only tell the controller if an authority */
+      control_event_general_status(LOG_WARN,
+                          "CLOCK_SKEW SKEW=%ld SOURCE=OR:%s:%d",
+                          apparent_skew,
+                          conn->_base.address, conn->_base.port);
   }
 
   /* XXX maybe act on my_apparent_addr, if the source is sufficiently

Modified: tor/trunk/src/or/config.c
===================================================================
--- tor/trunk/src/or/config.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/config.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -164,10 +164,11 @@
   V(BridgeRecordUsageByCountry,  BOOL,     "1"),
   V(BridgeRelay,                 BOOL,     "0"),
   V(CellStatistics,              BOOL,     "0"),
-  V(CircuitBuildTimeout,         INTERVAL, "1 minute"),
+  V(CircuitBuildTimeout,         INTERVAL, "0"),
   V(CircuitIdleTimeout,          INTERVAL, "1 hour"),
   V(ClientDNSRejectInternalAddresses, BOOL,"1"),
   V(ClientOnly,                  BOOL,     "0"),
+  V(ConsensusParams,             STRING,   NULL),
   V(ConnLimit,                   UINT,     "1000"),
   V(ConstrainedSockets,          BOOL,     "0"),
   V(ConstrainedSockSize,         MEMUNIT,  "8192"),
@@ -188,12 +189,10 @@
   V(DirPort,                     UINT,     "0"),
   V(DirPortFrontPage,            FILENAME, NULL),
   OBSOLETE("DirPostPeriod"),
-#ifdef ENABLE_DIRREQ_STATS
   OBSOLETE("DirRecordUsageByCountry"),
   OBSOLETE("DirRecordUsageGranularity"),
   OBSOLETE("DirRecordUsageRetainIPs"),
   OBSOLETE("DirRecordUsageSaveInterval"),
-#endif
   V(DirReqStatistics,            BOOL,     "0"),
   VAR("DirServer",               LINELIST, DirServers, NULL),
   V(DNSPort,                     UINT,     "0"),
@@ -210,6 +209,7 @@
   V(ExitPolicy,                  LINELIST, NULL),
   V(ExitPolicyRejectPrivate,     BOOL,     "1"),
   V(ExitPortStatistics,          BOOL,     "0"),
+  V(ExtraInfoStatistics,         BOOL,     "0"),
   V(FallbackNetworkstatusFile,   FILENAME,
     SHARE_DATADIR PATH_SEPARATOR "tor" PATH_SEPARATOR "fallback-consensus"),
   V(FascistFirewall,             BOOL,     "0"),
@@ -246,6 +246,10 @@
   V(HttpProxyAuthenticator,      STRING,   NULL),
   V(HttpsProxy,                  STRING,   NULL),
   V(HttpsProxyAuthenticator,     STRING,   NULL),
+  V(Socks4Proxy,                 STRING,   NULL),
+  V(Socks5Proxy,                 STRING,   NULL),
+  V(Socks5ProxyUsername,         STRING,   NULL),
+  V(Socks5ProxyPassword,         STRING,   NULL),
   OBSOLETE("IgnoreVersion"),
   V(KeepalivePeriod,             INTERVAL, "5 minutes"),
   VAR("Log",                     LINELIST, Logs,             NULL),
@@ -405,6 +409,10 @@
   V(LastRotatedOnionKey,              ISOTIME,  NULL),
   V(LastWritten,                      ISOTIME,  NULL),
 
+  V(TotalBuildTimes,                  UINT,     NULL),
+  VAR("CircuitBuildTimeBin",          LINELIST_S, BuildtimeHistogram, NULL),
+  VAR("BuildtimeHistogram",           LINELIST_V, BuildtimeHistogram, NULL),
+
   { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
 };
 
@@ -593,6 +601,10 @@
   /* Hidden service options: HiddenService: dir,excludenodes, nodes,
    * options, port.  PublishHidServDescriptor */
 
+  /* Circuit build time histogram options */
+  { "CircuitBuildTimeBin", "Histogram of recent circuit build times"},
+  { "TotalBuildTimes", "Total number of buildtimes in histogram"},
+
   /* Nonpersistent options: __LeaveStreamsUnattached, __AllDirActionsPrivate */
   { NULL, NULL },
 };
@@ -817,7 +829,7 @@
   return 0;
 }
 
-extern const char tor_svn_revision[]; /* from tor_main.c */
+extern const char tor_git_revision[]; /* from tor_main.c */
 
 /** The version of this Tor process, as parsed. */
 static char *_version = NULL;
@@ -827,10 +839,10 @@
 get_version(void)
 {
   if (_version == NULL) {
-    if (strlen(tor_svn_revision)) {
-      size_t len = strlen(VERSION)+strlen(tor_svn_revision)+8;
+    if (strlen(tor_git_revision)) {
+      size_t len = strlen(VERSION)+strlen(tor_git_revision)+16;
       _version = tor_malloc(len);
-      tor_snprintf(_version, len, "%s (r%s)", VERSION, tor_svn_revision);
+      tor_snprintf(_version, len, "%s (git-%s)", VERSION, tor_git_revision);
     } else {
       _version = tor_strdup(VERSION);
     }
@@ -1409,47 +1421,13 @@
     tor_free(actual_fname);
   }
 
-  if (options->DirReqStatistics) {
-#ifdef ENABLE_DIRREQ_STATS
+  if (options->DirReqStatistics && !geoip_is_loaded()) {
     /* Check if GeoIP database could be loaded. */
-    if (!geoip_is_loaded()) {
-      log_warn(LD_CONFIG, "Configured to measure directory request "
-               "statistics, but no GeoIP database found!");
-      return -1;
-    }
-    log_notice(LD_CONFIG, "Configured to count directory requests by "
-               "country and write aggregate statistics to disk. Check the "
-               "dirreq-stats file in your data directory that will first "
-               "be written in 24 hours from now.");
-#else
-  log_warn(LD_CONFIG, "DirReqStatistics enabled, but Tor was built "
-           "without support for directory request statistics.");
-#endif
+    log_warn(LD_CONFIG, "Configured to measure directory request "
+             "statistics, but no GeoIP database found!");
+    return -1;
   }
 
-#ifdef ENABLE_EXIT_STATS
-  if (options->ExitPortStatistics)
-    log_notice(LD_CONFIG, "Configured to measure exit port statistics. "
-               "Look for the exit-stats file that will first be written to "
-               "the data directory in 24 hours from now.");
-#else
-  if (options->ExitPortStatistics)
-    log_warn(LD_CONFIG, "ExitPortStatistics enabled, but Tor was built "
-             "without port statistics support.");
-#endif
-
-#ifdef ENABLE_BUFFER_STATS
-  if (options->CellStatistics)
-    log_notice(LD_CONFIG, "Configured to measure cell statistics. Look "
-               "for the buffer-stats file that will first be written to "
-               "the data directory in 24 hours from now.");
-#else
-  if (options->CellStatistics)
-    log_warn(LD_CONFIG, "CellStatistics enabled, but Tor was built "
-             "without cell statistics support.");
-#endif
-
-#ifdef ENABLE_ENTRY_STATS
   if (options->EntryStatistics) {
     if (should_record_bridge_info(options)) {
       /* Don't allow measuring statistics on entry guards when configured
@@ -1462,17 +1440,9 @@
       log_warn(LD_CONFIG, "Configured to measure entry node statistics, "
                "but no GeoIP database found!");
       return -1;
-    } else
-      log_notice(LD_CONFIG, "Configured to measure entry node "
-                 "statistics. Look for the entry-stats file that will "
-                 "first be written to the data directory in 24 hours "
-                 "from now.");
+    }
   }
-#else
-  if (options->EntryStatistics)
-    log_warn(LD_CONFIG, "EntryStatistics enabled, but Tor was built "
-             "without entry node statistics support.");
-#endif
+
   /* Check if we need to parse and add the EntryNodes config option. */
   if (options->EntryNodes &&
       (!old_options ||
@@ -1545,7 +1515,10 @@
                  fmt->abbrevs[i].abbreviated,
                  fmt->abbrevs[i].full);
       }
-      return fmt->abbrevs[i].full;
+      /* Keep going through the list in case we want to rewrite it more.
+       * (We could imagine recursing here, but I don't want to get the
+       * user into an infinite loop if we craft our list wrong.) */
+      option = fmt->abbrevs[i].full;
     }
   }
   return option;
@@ -2560,7 +2533,8 @@
      * the same /24 as last_resolved_addr will be the same as checking whether
      * it was on net 0, which is already done by is_internal_IP.
      */
-    if ((last_resolved_addr & 0xffffff00ul) == (ip & 0xffffff00ul))
+    if ((last_resolved_addr & (uint32_t)0xffffff00ul)
+        == (ip & (uint32_t)0xffffff00ul))
       return 1;
   }
   return 0;
@@ -2948,11 +2922,6 @@
 /** Highest allowable value for RendPostPeriod. */
 #define MAX_DIR_PERIOD (MIN_ONION_KEY_LIFETIME/2)
 
-/** Lowest allowable value for CircuitBuildTimeout; values too low will
- * increase network load because of failing connections being retried, and
- * might prevent users from connecting to the network at all. */
-#define MIN_CIRCUIT_BUILD_TIMEOUT 30
-
 /** Lowest allowable value for MaxCircuitDirtiness; if this is too low, Tor
  * will generate too many circuits and potentially overload the network. */
 #define MIN_MAX_CIRCUIT_DIRTINESS 10
@@ -3399,12 +3368,6 @@
     options->RendPostPeriod = MAX_DIR_PERIOD;
   }
 
-  if (options->CircuitBuildTimeout < MIN_CIRCUIT_BUILD_TIMEOUT) {
-    log(LOG_WARN, LD_CONFIG, "CircuitBuildTimeout option is too short; "
-      "raising to %d seconds.", MIN_CIRCUIT_BUILD_TIMEOUT);
-    options->CircuitBuildTimeout = MIN_CIRCUIT_BUILD_TIMEOUT;
-  }
-
   if (options->MaxCircuitDirtiness < MIN_MAX_CIRCUIT_DIRTINESS) {
     log(LOG_WARN, LD_CONFIG, "MaxCircuitDirtiness option is too short; "
       "raising to %d seconds.", MIN_MAX_CIRCUIT_DIRTINESS);
@@ -3482,7 +3445,7 @@
     REJECT("Failed to parse accounting options. See logs for details.");
 
   if (options->HttpProxy) { /* parse it now */
-    if (parse_addr_port(LOG_WARN, options->HttpProxy, NULL,
+    if (tor_addr_port_parse(options->HttpProxy,
                         &options->HttpProxyAddr, &options->HttpProxyPort) < 0)
       REJECT("HttpProxy failed to parse or resolve. Please fix.");
     if (options->HttpProxyPort == 0) { /* give it a default */
@@ -3496,7 +3459,7 @@
   }
 
   if (options->HttpsProxy) { /* parse it now */
-    if (parse_addr_port(LOG_WARN, options->HttpsProxy, NULL,
+    if (tor_addr_port_parse(options->HttpsProxy,
                         &options->HttpsProxyAddr, &options->HttpsProxyPort) <0)
       REJECT("HttpsProxy failed to parse or resolve. Please fix.");
     if (options->HttpsProxyPort == 0) { /* give it a default */
@@ -3509,6 +3472,45 @@
       REJECT("HttpsProxyAuthenticator is too long (>= 48 chars).");
   }
 
+  if (options->Socks4Proxy) { /* parse it now */
+    if (tor_addr_port_parse(options->Socks4Proxy,
+                        &options->Socks4ProxyAddr,
+                        &options->Socks4ProxyPort) <0)
+      REJECT("Socks4Proxy failed to parse or resolve. Please fix.");
+    if (options->Socks4ProxyPort == 0) { /* give it a default */
+      options->Socks4ProxyPort = 1080;
+    }
+  }
+
+  if (options->Socks5Proxy) { /* parse it now */
+    if (tor_addr_port_parse(options->Socks5Proxy,
+                            &options->Socks5ProxyAddr,
+                            &options->Socks5ProxyPort) <0)
+      REJECT("Socks5Proxy failed to parse or resolve. Please fix.");
+    if (options->Socks5ProxyPort == 0) { /* give it a default */
+      options->Socks5ProxyPort = 1080;
+    }
+  }
+
+  if (options->Socks4Proxy && options->Socks5Proxy)
+    REJECT("You cannot specify both Socks4Proxy and SOCKS5Proxy");
+
+  if (options->Socks5ProxyUsername) {
+    size_t len;
+
+    len = strlen(options->Socks5ProxyUsername);
+    if (len < 1 || len > 255)
+      REJECT("Socks5ProxyUsername must be between 1 and 255 characters.");
+
+    if (!options->Socks5ProxyPassword)
+      REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername.");
+
+    len = strlen(options->Socks5ProxyPassword);
+    if (len < 1 || len > 255)
+      REJECT("Socks5ProxyPassword must be between 1 and 255 characters.");
+  } else if (options->Socks5ProxyPassword)
+    REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername.");
+
   if (options->HashedControlPassword) {
     smartlist_t *sl = decode_hashed_passwords(options->HashedControlPassword);
     if (!sl) {
@@ -3657,6 +3659,12 @@
   if (options->PreferTunneledDirConns && !options->TunnelDirConns)
     REJECT("Must set TunnelDirConns if PreferTunneledDirConns is set.");
 
+  if ((options->Socks4Proxy || options->Socks5Proxy) &&
+      !options->HttpProxy && !options->PreferTunneledDirConns)
+    REJECT("When Socks4Proxy or Socks5Proxy is configured, "
+           "PreferTunneledDirConns and TunnelDirConns must both be "
+           "set to 1, or HttpProxy must be configured.");
+
   if (options->AutomapHostsSuffixes) {
     SMARTLIST_FOREACH(options->AutomapHostsSuffixes, char *, suf,
     {
@@ -3812,6 +3820,16 @@
     return -1;
   }
 
+  if (old->CellStatistics != new_val->CellStatistics ||
+      old->DirReqStatistics != new_val->DirReqStatistics ||
+      old->EntryStatistics != new_val->EntryStatistics ||
+      old->ExitPortStatistics != new_val->ExitPortStatistics) {
+    *msg = tor_strdup("While Tor is running, changing either "
+                      "CellStatistics, DirReqStatistics, EntryStatistics, "
+                      "or ExitPortStatistics is not allowed.");
+    return -1;
+  }
+
   return 0;
 }
 
@@ -4264,7 +4282,7 @@
  err:
   config_free(&options_format, newoptions);
   if (*msg) {
-    int len = strlen(*msg)+256;
+    int len = (int)strlen(*msg)+256;
     char *newmsg = tor_malloc(len);
 
     tor_snprintf(newmsg, len, "Failed to parse/validate config: %s", *msg);
@@ -4844,35 +4862,28 @@
   uint64_t v = 0;
   double d = 0;
   int use_float = 0;
+  char *cp;
 
-  smartlist_t *sl;
-
   tor_assert(ok);
-  sl = smartlist_create();
-  smartlist_split_string(sl, val, NULL,
-                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3);
 
-  if (smartlist_len(sl) < 1 || smartlist_len(sl) > 2) {
-    *ok = 0;
-    goto done;
-  }
-
-  v = tor_parse_uint64(smartlist_get(sl,0), 10, 0, UINT64_MAX, ok, NULL);
-  if (!*ok) {
-    int r = sscanf(smartlist_get(sl,0), "%lf", &d);
-    if (r == 0 || d < 0)
+  v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp);
+  if (!*ok || (cp && *cp == '.')) {
+    d = tor_parse_double(val, 0, UINT64_MAX, ok, &cp);
+    if (!*ok)
       goto done;
     use_float = 1;
   }
 
-  if (smartlist_len(sl) == 1) {
+  if (!cp) {
     *ok = 1;
     v = use_float ? DBL_TO_U64(d) :  v;
     goto done;
   }
 
+  cp = (char*) eat_whitespace(cp);
+
   for ( ;u->unit;++u) {
-    if (!strcasecmp(u->unit, smartlist_get(sl,1))) {
+    if (!strcasecmp(u->unit, cp)) {
       if (use_float)
         v = u->multiplier * d;
       else
@@ -4881,11 +4892,9 @@
       goto done;
     }
   }
-  log_warn(LD_CONFIG, "Unknown unit '%s'.", (char*)smartlist_get(sl,1));
+  log_warn(LD_CONFIG, "Unknown unit '%s'.", cp);
   *ok = 0;
  done:
-  SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
-  smartlist_free(sl);
 
   if (*ok)
     return v;
@@ -4900,7 +4909,8 @@
 static uint64_t
 config_parse_memunit(const char *s, int *ok)
 {
-  return config_parse_units(s, memory_units, ok);
+  uint64_t u = config_parse_units(s, memory_units, ok);
+  return u;
 }
 
 /** Parse a string in the format "number unit", where unit is a unit of time.
@@ -5050,6 +5060,10 @@
     log_warn(LD_GENERAL,"Unparseable bandwidth history state: %s",err);
     tor_free(err);
   }
+  if (circuit_build_times_parse_state(&circ_times, global_state, &err) < 0) {
+    log_warn(LD_GENERAL,"%s",err);
+    tor_free(err);
+  }
 }
 
 /** Reload the persistent state from disk, generating a new state as needed.
@@ -5182,6 +5196,7 @@
    * to avoid redundant writes. */
   entry_guards_update_state(global_state);
   rep_hist_update_state(global_state);
+  circuit_build_times_update_state(&circ_times, global_state);
   if (accounting_is_enabled(get_options()))
     accounting_run_housekeeping(now);
 

Modified: tor/trunk/src/or/connection.c
===================================================================
--- tor/trunk/src/or/connection.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/connection.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -32,6 +32,10 @@
 static void client_check_address_changed(int sock);
 static void set_constrained_socket_buffers(int sock, int size);
 
+static const char *connection_proxy_state_to_string(int state);
+static int connection_read_https_proxy_response(connection_t *conn);
+static void connection_send_socks5_connect(connection_t *conn);
+
 /** The last IPv4 address that our network interface seemed to have been
  * binding to, in host order.  We use this to detect when our IP changes. */
 static uint32_t last_interface_ip = 0;
@@ -92,8 +96,7 @@
     case CONN_TYPE_OR:
       switch (state) {
         case OR_CONN_STATE_CONNECTING: return "connect()ing";
-        case OR_CONN_STATE_PROXY_FLUSHING: return "proxy flushing";
-        case OR_CONN_STATE_PROXY_READING: return "proxy reading";
+        case OR_CONN_STATE_PROXY_HANDSHAKING: return "handshaking (proxy)";
         case OR_CONN_STATE_TLS_HANDSHAKING: return "handshaking (TLS)";
         case OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING:
           return "renegotiating (TLS)";
@@ -1289,6 +1292,353 @@
   return inprogress ? 0 : 1;
 }
 
+/** Convert state number to string representation for logging purposes.
+ */
+static const char *
+connection_proxy_state_to_string(int state)
+{
+  static const char *unknown = "???";
+  static const char *states[] = {
+    "PROXY_NONE",
+    "PROXY_HTTPS_WANT_CONNECT_OK",
+    "PROXY_SOCKS4_WANT_CONNECT_OK",
+    "PROXY_SOCKS5_WANT_AUTH_METHOD_NONE",
+    "PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929",
+    "PROXY_SOCKS5_WANT_AUTH_RFC1929_OK",
+    "PROXY_SOCKS5_WANT_CONNECT_OK",
+    "PROXY_CONNECTED",
+  };
+
+  if (state < PROXY_NONE || state > PROXY_CONNECTED)
+    return unknown;
+
+  return states[state];
+}
+
+/** Write a proxy request of <b>type</b> (socks4, socks5, https) to conn
+ * for conn->addr:conn->port, authenticating with the auth details given
+ * in the configuration (if available). SOCKS 5 and HTTP CONNECT proxies
+ * support authentication.
+ *
+ * Returns -1 if conn->addr is incompatible with the proxy protocol, and
+ * 0 otherwise.
+ *
+ * Use connection_read_proxy_handshake() to complete the handshake.
+ */
+int
+connection_proxy_connect(connection_t *conn, int type)
+{
+  or_options_t *options;
+
+  tor_assert(conn);
+
+  options = get_options();
+
+  switch (type) {
+    case PROXY_CONNECT: {
+      char buf[1024];
+      char *base64_authenticator=NULL;
+      const char *authenticator = options->HttpsProxyAuthenticator;
+
+      /* Send HTTP CONNECT and authentication (if available) in
+       * one request */
+
+      if (authenticator) {
+        base64_authenticator = alloc_http_authenticator(authenticator);
+        if (!base64_authenticator)
+          log_warn(LD_OR, "Encoding https authenticator failed");
+      }
+
+      if (base64_authenticator) {
+        tor_snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.1\r\n"
+                     "Proxy-Authorization: Basic %s\r\n\r\n",
+                     fmt_addr(&conn->addr),
+                     conn->port, base64_authenticator);
+        tor_free(base64_authenticator);
+      } else {
+        tor_snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n\r\n",
+                     fmt_addr(&conn->addr), conn->port);
+      }
+
+      connection_write_to_buf(buf, strlen(buf), conn);
+      conn->proxy_state = PROXY_HTTPS_WANT_CONNECT_OK;
+      break;
+    }
+
+    case PROXY_SOCKS4: {
+      unsigned char buf[9];
+      uint16_t portn;
+      uint32_t ip4addr;
+
+      /* Send a SOCKS4 connect request with empty user id */
+
+      if (tor_addr_family(&conn->addr) != AF_INET) {
+        log_warn(LD_NET, "SOCKS4 client is incompatible with with IPv6");
+        return -1;
+      }
+
+      ip4addr = tor_addr_to_ipv4n(&conn->addr);
+      portn = htons(conn->port);
+
+      buf[0] = 4; /* version */
+      buf[1] = SOCKS_COMMAND_CONNECT; /* command */
+      memcpy(buf + 2, &portn, 2); /* port */
+      memcpy(buf + 4, &ip4addr, 4); /* addr */
+      buf[8] = 0; /* userid (empty) */
+
+      connection_write_to_buf((char *)buf, sizeof(buf), conn);
+      conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK;
+      break;
+    }
+
+    case PROXY_SOCKS5: {
+      unsigned char buf[4]; /* fields: vers, num methods, method list */
+
+      /* Send a SOCKS5 greeting (connect request must wait) */
+
+      buf[0] = 5; /* version */
+
+      /* number of auth methods */
+      if (options->Socks5ProxyUsername) {
+        buf[1] = 2;
+        buf[2] = 0x00; /* no authentication */
+        buf[3] = 0x02; /* rfc1929 Username/Passwd auth */
+        conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929;
+      } else {
+        buf[1] = 1;
+        buf[2] = 0x00; /* no authentication */
+        conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_NONE;
+      }
+
+      connection_write_to_buf((char *)buf, 2 + buf[1], conn);
+      break;
+    }
+
+    default:
+      log_err(LD_BUG, "Invalid proxy protocol, %d", type);
+      tor_fragile_assert();
+      return -1;
+  }
+
+  log_debug(LD_NET, "set state %s",
+            connection_proxy_state_to_string(conn->proxy_state));
+
+  return 0;
+}
+
+/** Read conn's inbuf. If the http response from the proxy is all
+ * here, make sure it's good news, then return 1. If it's bad news,
+ * return -1. Else return 0 and hope for better luck next time.
+ */
+static int
+connection_read_https_proxy_response(connection_t *conn)
+{
+  char *headers;
+  char *reason=NULL;
+  int status_code;
+  time_t date_header;
+
+  switch (fetch_from_buf_http(conn->inbuf,
+                              &headers, MAX_HEADERS_SIZE,
+                              NULL, NULL, 10000, 0)) {
+    case -1: /* overflow */
+      log_warn(LD_PROTOCOL,
+               "Your https proxy sent back an oversized response. Closing.");
+      return -1;
+    case 0:
+      log_info(LD_NET,"https proxy response not all here yet. Waiting.");
+      return 0;
+    /* case 1, fall through */
+  }
+
+  if (parse_http_response(headers, &status_code, &date_header,
+                          NULL, &reason) < 0) {
+    log_warn(LD_NET,
+             "Unparseable headers from proxy (connecting to '%s'). Closing.",
+             conn->address);
+    tor_free(headers);
+    return -1;
+  }
+  if (!reason) reason = tor_strdup("[no reason given]");
+
+  if (status_code == 200) {
+    log_info(LD_NET,
+             "HTTPS connect to '%s' successful! (200 %s) Starting TLS.",
+             conn->address, escaped(reason));
+    tor_free(reason);
+    return 1;
+  }
+  /* else, bad news on the status code */
+  log_warn(LD_NET,
+           "The https proxy sent back an unexpected status code %d (%s). "
+           "Closing.",
+           status_code, escaped(reason));
+  tor_free(reason);
+  return -1;
+}
+
+/** Send SOCKS5 CONNECT command to <b>conn</b>, copying <b>conn->addr</b>
+ * and <b>conn->port</b> into the request.
+ */
+static void
+connection_send_socks5_connect(connection_t *conn)
+{
+  unsigned char buf[1024];
+  size_t reqsize = 6;
+  uint16_t port = htons(conn->port);
+
+  buf[0] = 5; /* version */
+  buf[1] = SOCKS_COMMAND_CONNECT; /* command */
+  buf[2] = 0; /* reserved */
+
+  if (tor_addr_family(&conn->addr) == AF_INET) {
+    uint32_t addr = tor_addr_to_ipv4n(&conn->addr);
+
+    buf[3] = 1;
+    reqsize += 4;
+    memcpy(buf + 4, &addr, 4);
+    memcpy(buf + 8, &port, 2);
+  } else { /* AF_INET6 */
+    buf[3] = 4;
+    reqsize += 16;
+    memcpy(buf + 4, tor_addr_to_in6(&conn->addr), 16);
+    memcpy(buf + 20, &port, 2);
+  }
+
+  connection_write_to_buf((char *)buf, reqsize, conn);
+
+  conn->proxy_state = PROXY_SOCKS5_WANT_CONNECT_OK;
+}
+
+/** Call this from connection_*_process_inbuf() to advance the proxy
+ * handshake.
+ *
+ * No matter what proxy protocol is used, if this function returns 1, the
+ * handshake is complete, and the data remaining on inbuf may contain the
+ * start of the communication with the requested server.
+ *
+ * Returns 0 if the current buffer contains an incomplete response, and -1
+ * on error.
+ */
+int
+connection_read_proxy_handshake(connection_t *conn)
+{
+  int ret = 0;
+  char *reason = NULL;
+
+  log_debug(LD_NET, "enter state %s",
+            connection_proxy_state_to_string(conn->proxy_state));
+
+  switch (conn->proxy_state) {
+    case PROXY_HTTPS_WANT_CONNECT_OK:
+      ret = connection_read_https_proxy_response(conn);
+      if (ret == 1)
+        conn->proxy_state = PROXY_CONNECTED;
+      break;
+
+    case PROXY_SOCKS4_WANT_CONNECT_OK:
+      ret = fetch_from_buf_socks_client(conn->inbuf,
+                                        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);
+      /* no auth needed, do connect */
+      if (ret == 1) {
+        connection_send_socks5_connect(conn);
+        ret = 0;
+      }
+      break;
+
+    case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929:
+      ret = fetch_from_buf_socks_client(conn->inbuf,
+                                        conn->proxy_state,
+                                        &reason);
+
+      /* send auth if needed, otherwise do connect */
+      if (ret == 1) {
+        connection_send_socks5_connect(conn);
+        ret = 0;
+      } else if (ret == 2) {
+        unsigned char buf[1024];
+        size_t reqsize, usize, psize;
+        const char *user, *pass;
+
+        user = get_options()->Socks5ProxyUsername;
+        pass = get_options()->Socks5ProxyPassword;
+        tor_assert(user && pass);
+
+        /* XXX len of user and pass must be <= 255 !!! */
+        usize = strlen(user);
+        psize = strlen(pass);
+        tor_assert(usize <= 255 && psize <= 255);
+        reqsize = 3 + usize + psize;
+
+        buf[0] = 1; /* negotiation version */
+        buf[1] = usize;
+        memcpy(buf + 2, user, usize);
+        buf[2 + usize] = psize;
+        memcpy(buf + 3 + usize, pass, psize);
+
+        connection_write_to_buf((char *)buf, reqsize, conn);
+
+        conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_RFC1929_OK;
+        ret = 0;
+      }
+      break;
+
+    case PROXY_SOCKS5_WANT_AUTH_RFC1929_OK:
+      ret = fetch_from_buf_socks_client(conn->inbuf,
+                                        conn->proxy_state,
+                                        &reason);
+      /* send the connect request */
+      if (ret == 1) {
+        connection_send_socks5_connect(conn);
+        ret = 0;
+      }
+      break;
+
+    case PROXY_SOCKS5_WANT_CONNECT_OK:
+      ret = fetch_from_buf_socks_client(conn->inbuf,
+                                        conn->proxy_state,
+                                        &reason);
+      if (ret == 1)
+        conn->proxy_state = PROXY_CONNECTED;
+      break;
+
+    default:
+      log_err(LD_BUG, "Invalid proxy_state for reading, %d",
+              conn->proxy_state);
+      tor_fragile_assert();
+      ret = -1;
+      break;
+  }
+
+  log_debug(LD_NET, "leaving state %s",
+            connection_proxy_state_to_string(conn->proxy_state));
+
+  if (ret < 0) {
+    if (reason) {
+      log_warn(LD_NET, "Proxy Client: unable to connect to %s:%d (%s)",
+                conn->address, conn->port, escaped(reason));
+      tor_free(reason);
+    } else {
+      log_warn(LD_NET, "Proxy Client: unable to connect to %s:%d",
+                conn->address, conn->port);
+    }
+  } else if (ret == 1) {
+    log_info(LD_NET, "Proxy Client: connection to %s:%d successful",
+              conn->address, conn->port);
+  }
+
+  return ret;
+}
+
 /**
  * Launch any configured listener connections of type <b>type</b>.  (A
  * listener is configured if <b>port_option</b> is non-zero.  If any
@@ -1704,12 +2054,12 @@
 
   if (num_read > 0) {
     if (conn->type == CONN_TYPE_EXIT)
-      rep_hist_note_exit_bytes_read(conn->port, num_read, now);
+      rep_hist_note_exit_bytes_read(conn->port, num_read);
     rep_hist_note_bytes_read(num_read, now);
   }
   if (num_written > 0) {
     if (conn->type == CONN_TYPE_EXIT)
-      rep_hist_note_exit_bytes_written(conn->port, num_written, now);
+      rep_hist_note_exit_bytes_written(conn->port, num_written);
     rep_hist_note_bytes_written(num_written, now);
   }
 
@@ -1996,7 +2346,7 @@
     return -1;
   }
   if (conn->linked_conn) {
-    /* The other side's handle_write will never actually get called, so
+    /* The other side's handle_write() will never actually get called, so
      * we need to invoke the appropriate callbacks ourself. */
     connection_t *linked = conn->linked_conn;
 
@@ -2013,7 +2363,7 @@
     if (!buf_datalen(linked->outbuf) && conn->active_on_link)
       connection_stop_reading_from_linked_conn(conn);
   }
-  /* If we hit the EOF, call connection_reached_eof. */
+  /* If we hit the EOF, call connection_reached_eof(). */
   if (!conn->marked_for_close &&
       conn->inbuf_reached_eof &&
       connection_reached_eof(conn) < 0) {
@@ -2055,7 +2405,7 @@
   }
 
   if (connection_speaks_cells(conn) &&
-      conn->state > OR_CONN_STATE_PROXY_READING) {
+      conn->state > OR_CONN_STATE_PROXY_HANDSHAKING) {
     int pending;
     or_connection_t *or_conn = TO_OR_CONN(conn);
     size_t initial_size;
@@ -2239,7 +2589,7 @@
     return 0; /* do nothing */
 
   if (conn->in_flushed_some) {
-    log_warn(LD_BUG, "called recursively from inside conn->in_flushed_some()");
+    log_warn(LD_BUG, "called recursively from inside conn->in_flushed_some");
     return 0;
   }
 
@@ -2283,7 +2633,7 @@
     : connection_bucket_write_limit(conn, now);
 
   if (connection_speaks_cells(conn) &&
-      conn->state > OR_CONN_STATE_PROXY_READING) {
+      conn->state > OR_CONN_STATE_PROXY_HANDSHAKING) {
     or_connection_t *or_conn = TO_OR_CONN(conn);
     if (conn->state == OR_CONN_STATE_TLS_HANDSHAKING ||
         conn->state == OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING) {
@@ -2302,13 +2652,13 @@
     /* else open, or closing */
     result = flush_buf_tls(or_conn->tls, conn->outbuf,
                            max_to_write, &conn->outbuf_flushlen);
-#ifdef ENABLE_DIRREQ_STATS
+
     /* If we just flushed the last bytes, check if this tunneled dir
      * request is done. */
     if (buf_datalen(conn->outbuf) == 0 && conn->dirreq_id)
       geoip_change_dirreq_state(conn->dirreq_id, DIRREQ_TUNNELED,
                                 DIRREQ_OR_CONN_BUFFER_FLUSHED);
-#endif
+
     switch (result) {
       CASE_TOR_TLS_ERROR_ANY:
       case TOR_TLS_CLOSE:
@@ -2328,8 +2678,8 @@
         if (!connection_is_reading(conn)) {
           connection_stop_writing(conn);
           conn->write_blocked_on_bw = 1;
-          /* we'll start reading again when the next second arrives,
-           * and then also start writing again.
+          /* we'll start reading again when we get more tokens in our
+           * read bucket; then we'll start writing again too.
            */
         }
         /* else no problem, we're already reading */
@@ -2717,7 +3067,7 @@
     return;
   }
 
-  /* Okay.  If we've used this address previously, we're okay. */
+  /* If we've used this address previously, we're okay. */
   ip_out = ntohl(out_addr.sin_addr.s_addr);
   SMARTLIST_FOREACH(outgoing_addrs, uint32_t*, ip_ptr,
                     if (*ip_ptr == ip_out) return;
@@ -3016,7 +3366,7 @@
     }
 //    tor_assert(conn->addr && conn->port);
     tor_assert(conn->address);
-    if (conn->state > OR_CONN_STATE_PROXY_READING)
+    if (conn->state > OR_CONN_STATE_PROXY_HANDSHAKING)
       tor_assert(or_conn->tls);
   }
 

Modified: tor/trunk/src/or/connection_edge.c
===================================================================
--- tor/trunk/src/or/connection_edge.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/connection_edge.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -333,7 +333,7 @@
            escaped_safe_str(conn->address),conn->port,
            safe_str(fmt_addr(&conn->addr)));
 
-  rep_hist_note_exit_stream_opened(conn->port, approx_time());
+  rep_hist_note_exit_stream_opened(conn->port);
 
   conn->state = EXIT_CONN_STATE_OPEN;
   connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */
@@ -2544,11 +2544,11 @@
 
   log_debug(LD_EXIT,"Creating new exit connection.");
   n_stream = edge_connection_new(CONN_TYPE_EXIT, AF_INET);
-#ifdef ENABLE_DIRREQ_STATS
+
   /* Remember the tunneled request ID in the new edge connection, so that
    * we can measure download times. */
   TO_CONN(n_stream)->dirreq_id = circ->dirreq_id;
-#endif
+
   n_stream->_base.purpose = EXIT_PURPOSE_CONNECT;
 
   n_stream->stream_id = rh.stream_id;
@@ -2785,11 +2785,10 @@
   dirconn->_base.purpose = DIR_PURPOSE_SERVER;
   dirconn->_base.state = DIR_CONN_STATE_SERVER_COMMAND_WAIT;
 
-#ifdef ENABLE_DIRREQ_STATS
   /* Note that the new dir conn belongs to the same tunneled request as
    * the edge conn, so that we can measure download times. */
   TO_CONN(dirconn)->dirreq_id = TO_CONN(exitconn)->dirreq_id;
-#endif
+
   connection_link_connections(TO_CONN(dirconn), TO_CONN(exitconn));
 
   if (connection_add(TO_CONN(exitconn))<0) {

Modified: tor/trunk/src/or/connection_or.c
===================================================================
--- tor/trunk/src/or/connection_or.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/connection_or.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -187,66 +187,6 @@
   return 0;
 }
 
-/** Read conn's inbuf. If the http response from the proxy is all
- * here, make sure it's good news, and begin the tls handshake. If
- * it's bad news, close the connection and return -1. Else return 0
- * and hope for better luck next time.
- */
-static int
-connection_or_read_proxy_response(or_connection_t *or_conn)
-{
-  char *headers;
-  char *reason=NULL;
-  int status_code;
-  time_t date_header;
-  connection_t *conn = TO_CONN(or_conn);
-
-  switch (fetch_from_buf_http(conn->inbuf,
-                              &headers, MAX_HEADERS_SIZE,
-                              NULL, NULL, 10000, 0)) {
-    case -1: /* overflow */
-      log_warn(LD_PROTOCOL,
-               "Your https proxy sent back an oversized response. Closing.");
-      return -1;
-    case 0:
-      log_info(LD_OR,"https proxy response not all here yet. Waiting.");
-      return 0;
-    /* case 1, fall through */
-  }
-
-  if (parse_http_response(headers, &status_code, &date_header,
-                          NULL, &reason) < 0) {
-    log_warn(LD_OR,
-             "Unparseable headers from proxy (connecting to '%s'). Closing.",
-             conn->address);
-    tor_free(headers);
-    return -1;
-  }
-  if (!reason) reason = tor_strdup("[no reason given]");
-
-  if (status_code == 200) {
-    log_info(LD_OR,
-             "HTTPS connect to '%s' successful! (200 %s) Starting TLS.",
-             conn->address, escaped(reason));
-    tor_free(reason);
-    if (connection_tls_start_handshake(or_conn, 0) < 0) {
-      /* TLS handshaking error of some kind. */
-      connection_mark_for_close(conn);
-
-      return -1;
-    }
-    return 0;
-  }
-  /* else, bad news on the status code */
-  log_warn(LD_OR,
-           "The https proxy sent back an unexpected status code %d (%s). "
-           "Closing.",
-           status_code, escaped(reason));
-  tor_free(reason);
-  connection_mark_for_close(conn);
-  return -1;
-}
-
 /** Handle any new bytes that have come in on connection <b>conn</b>.
  * If conn is in 'open' state, hand it to
  * connection_or_process_cells_from_inbuf()
@@ -255,11 +195,24 @@
 int
 connection_or_process_inbuf(or_connection_t *conn)
 {
+  int ret;
   tor_assert(conn);
 
   switch (conn->_base.state) {
-    case OR_CONN_STATE_PROXY_READING:
-      return connection_or_read_proxy_response(conn);
+    case OR_CONN_STATE_PROXY_HANDSHAKING:
+      ret = connection_read_proxy_handshake(TO_CONN(conn));
+
+      /* start TLS after handshake completion, or deal with error */
+      if (ret == 1) {
+        tor_assert(TO_CONN(conn)->proxy_state == PROXY_CONNECTED);
+        if (connection_tls_start_handshake(conn, 0) < 0)
+          ret = -1;
+      }
+      if (ret < 0) {
+        connection_mark_for_close(TO_CONN(conn));
+      }
+
+      return ret;
     case OR_CONN_STATE_OPEN:
     case OR_CONN_STATE_OR_HANDSHAKING:
       return connection_or_process_cells_from_inbuf(conn);
@@ -312,11 +265,7 @@
   assert_connection_ok(TO_CONN(conn),0);
 
   switch (conn->_base.state) {
-    case OR_CONN_STATE_PROXY_FLUSHING:
-      log_debug(LD_OR,"finished sending CONNECT to proxy.");
-      conn->_base.state = OR_CONN_STATE_PROXY_READING;
-      connection_stop_writing(TO_CONN(conn));
-      break;
+    case OR_CONN_STATE_PROXY_HANDSHAKING:
     case OR_CONN_STATE_OPEN:
     case OR_CONN_STATE_OR_HANDSHAKING:
       connection_stop_writing(TO_CONN(conn));
@@ -334,37 +283,34 @@
 int
 connection_or_finished_connecting(or_connection_t *or_conn)
 {
+  int proxy_type;
   connection_t *conn;
   tor_assert(or_conn);
   conn = TO_CONN(or_conn);
   tor_assert(conn->state == OR_CONN_STATE_CONNECTING);
 
-  log_debug(LD_OR,"OR connect() to router at %s:%u finished.",
+  log_debug(LD_HANDSHAKE,"OR connect() to router at %s:%u finished.",
             conn->address,conn->port);
   control_event_bootstrap(BOOTSTRAP_STATUS_HANDSHAKE, 0);
 
-  if (get_options()->HttpsProxy) {
-    char buf[1024];
-    char *base64_authenticator=NULL;
-    const char *authenticator = get_options()->HttpsProxyAuthenticator;
+  proxy_type = PROXY_NONE;
 
-    if (authenticator) {
-      base64_authenticator = alloc_http_authenticator(authenticator);
-      if (!base64_authenticator)
-        log_warn(LD_OR, "Encoding https authenticator failed");
+  if (get_options()->HttpsProxy)
+    proxy_type = PROXY_CONNECT;
+  else if (get_options()->Socks4Proxy)
+    proxy_type = PROXY_SOCKS4;
+  else if (get_options()->Socks5Proxy)
+    proxy_type = PROXY_SOCKS5;
+
+  if (proxy_type != PROXY_NONE) {
+    /* start proxy handshake */
+    if (connection_proxy_connect(conn, proxy_type) < 0) {
+      connection_mark_for_close(conn);
+      return -1;
     }
-    if (base64_authenticator) {
-      tor_snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.1\r\n"
-                   "Proxy-Authorization: Basic %s\r\n\r\n",
-                   fmt_addr(&conn->addr),
-                   conn->port, base64_authenticator);
-      tor_free(base64_authenticator);
-    } else {
-      tor_snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n\r\n",
-                   fmt_addr(&conn->addr), conn->port);
-    }
-    connection_write_to_buf(buf, strlen(buf), conn);
-    conn->state = OR_CONN_STATE_PROXY_FLUSHING;
+
+    connection_start_reading(conn);
+    conn->state = OR_CONN_STATE_PROXY_HANDSHAKING;
     return 0;
   }
 
@@ -753,6 +699,7 @@
   or_connection_t *conn;
   or_options_t *options = get_options();
   int socket_error = 0;
+  int using_proxy = 0;
   tor_addr_t addr;
 
   tor_assert(_addr);
@@ -771,19 +718,27 @@
   conn->_base.state = OR_CONN_STATE_CONNECTING;
   control_event_or_conn_status(conn, OR_CONN_EVENT_LAUNCHED, 0);
 
+  /* use a proxy server if available */
   if (options->HttpsProxy) {
-    /* we shouldn't connect directly. use the https proxy instead. */
-    tor_addr_from_ipv4h(&addr, options->HttpsProxyAddr);
+    using_proxy = 1;
+    tor_addr_copy(&addr, &options->HttpsProxyAddr);
     port = options->HttpsProxyPort;
+  } else if (options->Socks4Proxy) {
+    using_proxy = 1;
+    tor_addr_copy(&addr, &options->Socks4ProxyAddr);
+    port = options->Socks4ProxyPort;
+  } else if (options->Socks5Proxy) {
+    using_proxy = 1;
+    tor_addr_copy(&addr, &options->Socks5ProxyAddr);
+    port = options->Socks5ProxyPort;
   }
 
   switch (connection_connect(TO_CONN(conn), conn->_base.address,
                              &addr, port, &socket_error)) {
     case -1:
       /* If the connection failed immediately, and we're using
-       * an https proxy, our https proxy is down. Don't blame the
-       * Tor server. */
-      if (!options->HttpsProxy)
+       * a proxy, our proxy is down. Don't blame the Tor server. */
+      if (!using_proxy)
         entry_guard_register_connect_status(conn->identity_digest,
                                             0, 1, time(NULL));
       connection_or_connect_failed(conn,
@@ -825,7 +780,7 @@
     return -1;
   }
   connection_start_reading(TO_CONN(conn));
-  log_debug(LD_OR,"starting TLS handshake on fd %d", conn->_base.s);
+  log_debug(LD_HANDSHAKE,"starting TLS handshake on fd %d", conn->_base.s);
   note_crypto_pk_op(receiving ? TLS_HANDSHAKE_S : TLS_HANDSHAKE_C);
 
   if (connection_tls_continue_handshake(conn) < 0) {
@@ -965,12 +920,12 @@
   check_no_tls_errors();
   has_cert = tor_tls_peer_has_cert(conn->tls);
   if (started_here && !has_cert) {
-    log_info(LD_PROTOCOL,"Tried connecting to router at %s:%d, but it didn't "
+    log_info(LD_HANDSHAKE,"Tried connecting to router at %s:%d, but it didn't "
              "send a cert! Closing.",
              safe_address, conn->_base.port);
     return -1;
   } else if (!has_cert) {
-    log_debug(LD_PROTOCOL,"Got incoming connection with no certificate. "
+    log_debug(LD_HANDSHAKE,"Got incoming connection with no certificate. "
               "That's ok.");
   }
   check_no_tls_errors();
@@ -979,15 +934,16 @@
     int v = tor_tls_verify(started_here?severity:LOG_INFO,
                            conn->tls, &identity_rcvd);
     if (started_here && v<0) {
-      log_fn(severity,LD_OR,"Tried connecting to router at %s:%d: It"
+      log_fn(severity,LD_HANDSHAKE,"Tried connecting to router at %s:%d: It"
              " has a cert but it's invalid. Closing.",
              safe_address, conn->_base.port);
         return -1;
     } else if (v<0) {
-      log_info(LD_PROTOCOL,"Incoming connection gave us an invalid cert "
+      log_info(LD_HANDSHAKE,"Incoming connection gave us an invalid cert "
                "chain; ignoring.");
     } else {
-      log_debug(LD_OR,"The certificate seems to be valid on %s connection "
+      log_debug(LD_HANDSHAKE,
+                "The certificate seems to be valid on %s connection "
                 "with %s:%d", conn_type, safe_address, conn->_base.port);
     }
     check_no_tls_errors();
@@ -1014,7 +970,7 @@
     conn->nickname[0] = '$';
     base16_encode(conn->nickname+1, HEX_DIGEST_LEN+1,
                   conn->identity_digest, DIGEST_LEN);
-    log_info(LD_OR, "Connected to router %s at %s:%d without knowing "
+    log_info(LD_HANDSHAKE, "Connected to router %s at %s:%d without knowing "
                     "its key. Hoping for the best.",
                     conn->nickname, conn->_base.address, conn->_base.port);
   }
@@ -1030,7 +986,7 @@
       base16_encode(seen, sizeof(seen), digest_rcvd_out, DIGEST_LEN);
       base16_encode(expected, sizeof(expected), conn->identity_digest,
                     DIGEST_LEN);
-      log_fn(severity, LD_OR,
+      log_fn(severity, LD_HANDSHAKE,
              "Tried connecting to router at %s:%d, but identity key was not "
              "as expected: wanted %s but got %s.",
              conn->_base.address, conn->_base.port, expected, seen);
@@ -1072,7 +1028,7 @@
   char digest_rcvd[DIGEST_LEN];
   int started_here = connection_or_nonopen_was_started_here(conn);
 
-  log_debug(LD_OR,"tls handshake with %s done. verifying.",
+  log_debug(LD_HANDSHAKE,"tls handshake with %s done. verifying.",
             safe_str(conn->_base.address));
 
   directory_set_dirty();
@@ -1081,6 +1037,8 @@
                                               digest_rcvd) < 0)
     return -1;
 
+  circuit_build_times_network_is_live(&circ_times);
+
   if (tor_tls_used_v1_handshake(conn->tls)) {
     conn->link_proto = 1;
     if (!started_here) {
@@ -1132,6 +1090,7 @@
   control_event_or_conn_status(conn, OR_CONN_EVENT_CONNECTED, 0);
 
   if (started_here) {
+    circuit_build_times_network_is_live(&circ_times);
     rep_hist_note_connect_succeeded(conn->identity_digest, now);
     if (entry_guard_register_connect_status(conn->identity_digest,
                                             1, 0, now) < 0) {
@@ -1232,6 +1191,7 @@
     if (connection_fetch_var_cell_from_buf(conn, &var_cell)) {
       if (!var_cell)
         return 0; /* not yet. */
+      circuit_build_times_network_is_live(&circ_times);
       command_process_var_cell(var_cell, conn);
       var_cell_free(var_cell);
     } else {
@@ -1241,6 +1201,7 @@
                                                                  available? */
         return 0; /* not yet */
 
+      circuit_build_times_network_is_live(&circ_times);
       connection_fetch_from_buf(buf, CELL_NETWORK_SIZE, TO_CONN(conn));
 
       /* retrieve cell info from buf (create the host-order struct from the

Modified: tor/trunk/src/or/control.c
===================================================================
--- tor/trunk/src/or/control.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/control.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -1696,7 +1696,11 @@
       *answer = tor_strdup(has_completed_circuit ? "1" : "0");
     } else if (!strcmp(question, "status/enough-dir-info")) {
       *answer = tor_strdup(router_have_minimum_dir_info() ? "1" : "0");
-    } else if (!strcmp(question, "status/good-server-descriptor")) {
+    } else if (!strcmp(question, "status/good-server-descriptor") ||
+               !strcmp(question, "status/accepted-server-descriptor")) {
+      /* They're equivalent for now, until we can figure out how to make
+       * good-server-descriptor be what we want. See comment in
+       * control-spec.txt. */
       *answer = tor_strdup(directories_have_accepted_server_descriptor()
                            ? "1" : "0");
     } else if (!strcmp(question, "status/reachability-succeeded/or")) {
@@ -2495,7 +2499,7 @@
   int is_reverse = 0;
   (void) len; /* body is nul-terminated; it's safe to ignore the length */
 
-  if (!(conn->event_mask & (1L<<EVENT_ADDRMAP))) {
+  if (!(conn->event_mask & ((uint32_t)1L<<EVENT_ADDRMAP))) {
     log_warn(LD_CONTROL, "Controller asked us to resolve an address, but "
              "isn't listening for ADDRMAP events.  It probably won't see "
              "the answer.");

Modified: tor/trunk/src/or/directory.c
===================================================================
--- tor/trunk/src/or/directory.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/directory.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -554,11 +554,6 @@
 connection_dir_request_failed(dir_connection_t *conn)
 {
   if (directory_conn_is_self_reachability_test(conn)) {
-    routerinfo_t *me = router_get_my_routerinfo();
-    if (me)
-      control_event_server_status(LOG_WARN,
-                                  "REACHABILITY_FAILED DIRADDRESS=%s:%d",
-                                  me->address, me->dir_port);
     return; /* this was a test fetch. don't retry. */
   }
   if (entry_list_can_grow(get_options()))
@@ -747,6 +742,15 @@
 
   log_debug(LD_DIR, "Initiating %s", dir_conn_purpose_to_string(dir_purpose));
 
+  /* ensure that we don't make direct connections when a SOCKS server is
+   * configured. */
+  if (!anonymized_connection && !use_begindir && !options->HttpProxy &&
+      (options->Socks4Proxy || options->Socks5Proxy)) {
+    log_warn(LD_DIR, "Cannot connect to a directory server through a "
+                     "SOCKS proxy!");
+    return;
+  }
+
   conn = dir_connection_new(AF_INET);
 
   /* set up conn so it's got all the data we need to remember */
@@ -772,7 +776,7 @@
     /* then we want to connect to dirport directly */
 
     if (options->HttpProxy) {
-      tor_addr_from_ipv4h(&addr, options->HttpProxyAddr);
+      tor_addr_copy(&addr, &options->HttpProxyAddr);
       dir_port = options->HttpProxyPort;
     }
 
@@ -877,7 +881,7 @@
 directory_get_consensus_url(int supports_conditional_consensus)
 {
   char *url;
-  int len;
+  size_t len;
 
   if (supports_conditional_consensus) {
     char *authority_id_list;
@@ -1649,6 +1653,8 @@
              "'%s:%d'",(int) body_len, conn->_base.address, conn->_base.port);
     if (trusted_dirs_load_certs_from_string(body, 0, 1)<0) {
       log_warn(LD_DIR, "Unable to parse fetched certificates");
+      /* if we fetched more than one and only some failed, the successful
+       * ones got flushed to disk so it's safe to call this on them */
       connection_dir_download_cert_failed(conn, status_code);
     } else {
       directory_info_has_arrived(now, 0);
@@ -2326,7 +2332,7 @@
   need_at_least = smartlist_len(want_authorities)/2+1;
   SMARTLIST_FOREACH(want_authorities, const char *, d, {
     char want_digest[DIGEST_LEN];
-    int want_len = strlen(d)/2;
+    size_t want_len = strlen(d)/2;
     if (want_len > DIGEST_LEN)
       want_len = DIGEST_LEN;
 
@@ -2562,7 +2568,6 @@
       goto done;
     }
 
-#ifdef ENABLE_DIRREQ_STATS
     {
       struct in_addr in;
       if (tor_inet_aton((TO_CONN(conn))->address, &in)) {
@@ -2578,7 +2583,6 @@
                              DIRREQ_DIRECT);
       }
     }
-#endif
 
     // note_request(request_type,dlen);
     (void) request_type;
@@ -3210,7 +3214,6 @@
   tor_assert(conn);
   tor_assert(conn->_base.type == CONN_TYPE_DIR);
 
-#ifdef ENABLE_DIRREQ_STATS
   /* Note that we have finished writing the directory response. For direct
    * connections this means we're done, for tunneled connections its only
    * an intermediate step. */
@@ -3221,7 +3224,6 @@
     geoip_change_dirreq_state(TO_CONN(conn)->global_identifier,
                               DIRREQ_DIRECT,
                               DIRREQ_FLUSHING_DIR_CONN_FINISHED);
-#endif
   switch (conn->_base.state) {
     case DIR_CONN_STATE_CLIENT_SENDING:
       log_debug(LD_DIR,"client finished sending command.");

Modified: tor/trunk/src/or/dirserv.c
===================================================================
--- tor/trunk/src/or/dirserv.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/dirserv.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -1864,7 +1864,7 @@
  *   NS_V2 - Output an entry suitable for a V2 NS opinion document
  *   NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry
  *   NS_V3_VOTE - Output a complete V3 NS vote
- *   NS_CONTROL_PORT - Output a NS docunent for the control port
+ *   NS_CONTROL_PORT - Output a NS document for the control port
  */
 int
 routerstatus_format_entry(char *buf, size_t buf_len,
@@ -2324,7 +2324,7 @@
 
   if (rs) {
     rs->has_measured_bw = 1;
-    rs->measured_bw = parsed_line->bw;
+    rs->measured_bw = (uint32_t)parsed_line->bw;
   } else {
     log_info(LD_DIRSERV, "Node ID %s not found in routerstatus list",
              parsed_line->node_hex);
@@ -2390,9 +2390,9 @@
   }
 
   fclose(fp);
-  log_notice(LD_DIRSERV,
-             "Bandwidth measurement file successfully read. "
-             "Applied %d measurements.", applied_lines);
+  log_info(LD_DIRSERV,
+           "Bandwidth measurement file successfully read. "
+           "Applied %d measurements.", applied_lines);
   return 0;
 }
 
@@ -2553,6 +2553,13 @@
   }
   smartlist_sort_strings(v3_out->known_flags);
 
+  if (options->ConsensusParams) {
+    v3_out->net_params = smartlist_create();
+    smartlist_split_string(v3_out->net_params,
+                           options->ConsensusParams, NULL, 0, 0);
+    smartlist_sort_strings(v3_out->net_params);
+  }
+
   voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
   voter->nickname = tor_strdup(options->Nickname);
   memcpy(voter->identity_digest, identity_digest, DIGEST_LEN);

Modified: tor/trunk/src/or/dirvote.c
===================================================================
--- tor/trunk/src/or/dirvote.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/dirvote.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -21,7 +21,13 @@
 static void dirvote_clear_votes(int all_votes);
 static int dirvote_compute_consensus(void);
 static int dirvote_publish_consensus(void);
+static char *make_consensus_method_list(int low, int high);
 
+/** The highest consensus method that we currently support. */
+#define MAX_SUPPORTED_CONSENSUS_METHOD 7
+
+#define MIN_METHOD_FOR_PARAMS 7
+
 /* =====
  * Voting
  * =====*/
@@ -93,20 +99,25 @@
     char fu[ISO_TIME_LEN+1];
     char vu[ISO_TIME_LEN+1];
     char *flags = smartlist_join_strings(v3_ns->known_flags, " ", 0, NULL);
+    char *params;
     authority_cert_t *cert = v3_ns->cert;
+    char *methods =
+      make_consensus_method_list(1, MAX_SUPPORTED_CONSENSUS_METHOD);
     format_iso_time(published, v3_ns->published);
     format_iso_time(va, v3_ns->valid_after);
     format_iso_time(fu, v3_ns->fresh_until);
     format_iso_time(vu, v3_ns->valid_until);
 
+    if (v3_ns->net_params)
+      params = smartlist_join_strings(v3_ns->net_params, " ", 0, NULL);
+    else
+      params = tor_strdup("");
+
     tor_assert(cert);
     tor_snprintf(status, len,
                  "network-status-version 3\n"
                  "vote-status %s\n"
-                 /* XXX: If you change this value, you also need to
-                  * change consensus_method_is_supported().
-                  * Perhaps we should unify these somehow? */
-                 "consensus-methods 1 2 3 4 5 6\n"
+                 "consensus-methods %s\n"
                  "published %s\n"
                  "valid-after %s\n"
                  "fresh-until %s\n"
@@ -114,17 +125,22 @@
                  "voting-delay %d %d\n"
                  "%s" /* versions */
                  "known-flags %s\n"
+                 "params %s\n"
                  "dir-source %s %s %s %s %d %d\n"
                  "contact %s\n",
                  v3_ns->type == NS_TYPE_VOTE ? "vote" : "opinion",
+                 methods,
                  published, va, fu, vu,
                  v3_ns->vote_seconds, v3_ns->dist_seconds,
                  version_lines,
                  flags,
+                 params,
                  voter->nickname, fingerprint, voter->address,
                    ipaddr, voter->dir_port, voter->or_port, voter->contact);
 
+    tor_free(params);
     tor_free(flags);
+    tor_free(methods);
     outp = status + strlen(status);
     endp = status + len;
 
@@ -458,12 +474,33 @@
 static int
 consensus_method_is_supported(int method)
 {
-  /* XXX: If you change this value, you also need to change
-   * format_networkstatus_vote(). Perhaps we should unify
-   * these somehow? */
-  return (method >= 1) && (method <= 6);
+  return (method >= 1) && (method <= MAX_SUPPORTED_CONSENSUS_METHOD);
 }
 
+/** Return a newly allocated string holding the numbers between low and high
+ * (inclusive) that are supported consensus methods. */
+static char *
+make_consensus_method_list(int low, int high)
+{
+  char *list;
+
+  char b[32];
+  int i;
+  smartlist_t *lst;
+  lst = smartlist_create();
+  for (i = low; i <= high; ++i) {
+    if (!consensus_method_is_supported(i))
+      continue;
+    tor_snprintf(b, sizeof(b), "%d", i);
+    smartlist_add(lst, tor_strdup(b));
+  }
+  list = smartlist_join_strings(lst, " ", 0, NULL);
+  tor_assert(list);
+  SMARTLIST_FOREACH(lst, char *, cp, tor_free(cp));
+  smartlist_free(lst);
+  return list;
+}
+
 /** Helper: given <b>lst</b>, a list of version strings such that every
  * version appears once for every versioning voter who recommends it, return a
  * newly allocated string holding the resulting client-versions or
@@ -481,6 +518,89 @@
   return result;
 }
 
+/** Helper: given a list of valid networkstatus_t, return a new string
+ * containing the contents of the consensus network parameter set.
+ */
+/* private */ char *
+dirvote_compute_params(smartlist_t *votes)
+{
+  int i;
+  int32_t *vals;
+
+  int cur_param_len;
+  const char *cur_param;
+  const char *eq;
+  char *result;
+
+  const int n_votes = smartlist_len(votes);
+  smartlist_t *output;
+  smartlist_t *param_list = smartlist_create();
+
+  /* We require that the parameter lists in the votes are well-formed: that
+     is, that their keywords are unique and sorted, and that their values are
+     between INT32_MIN and INT32_MAX inclusive.  This should be guaranteed by
+     the parsing code. */
+
+  vals = tor_malloc(sizeof(int)*n_votes);
+
+  SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) {
+    if (!v->net_params)
+      continue;
+    smartlist_add_all(param_list, v->net_params);
+  } SMARTLIST_FOREACH_END(v);
+
+  if (smartlist_len(param_list) == 0) {
+    tor_free(vals);
+    smartlist_free(param_list);
+    return NULL;
+  }
+
+  smartlist_sort_strings(param_list);
+  i = 0;
+  cur_param = smartlist_get(param_list, 0);
+  eq = strchr(cur_param, '=');
+  tor_assert(eq);
+  cur_param_len = (int)(eq+1 - cur_param);
+
+  output = smartlist_create();
+
+  SMARTLIST_FOREACH_BEGIN(param_list, const char *, param) {
+    const char *next_param;
+    int ok=0;
+    eq = strchr(param, '=');
+    tor_assert(i<n_votes);
+    vals[i++] = (int32_t)
+      tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
+    tor_assert(ok);
+
+    if (param_sl_idx+1 == smartlist_len(param_list))
+      next_param = NULL;
+    else
+      next_param = smartlist_get(param_list, param_sl_idx+1);
+    if (!next_param || strncmp(next_param, param, cur_param_len)) {
+      /* We've reached the end of a series. */
+      int32_t median = median_int32(vals, i);
+      char *out_string = tor_malloc(64+cur_param_len);
+      memcpy(out_string, param, cur_param_len);
+      tor_snprintf(out_string+cur_param_len,64, "%ld", (long)median);
+      smartlist_add(output, out_string);
+
+      i = 0;
+      if (next_param) {
+        eq = strchr(next_param, '=');
+        cur_param_len = (int)(eq+1 - next_param);
+      }
+    }
+  } SMARTLIST_FOREACH_END(param);
+
+  result = smartlist_join_strings(output, " ", 0, NULL);
+  SMARTLIST_FOREACH(output, char *, cp, tor_free(cp));
+  smartlist_free(output);
+  smartlist_free(param_list);
+  tor_free(vals);
+  return result;
+}
+
 /** Given a list of vote networkstatus_t in <b>votes</b>, our public
  * authority <b>identity_key</b>, our private authority <b>signing_key</b>,
  * and the number of <b>total_authorities</b> that we believe exist in our
@@ -633,6 +753,15 @@
     tor_free(flaglist);
   }
 
+  if (consensus_method >= MIN_METHOD_FOR_PARAMS) {
+    char *params = dirvote_compute_params(votes);
+    if (params) {
+      smartlist_add(chunks, tor_strdup("params "));
+      smartlist_add(chunks, params);
+      smartlist_add(chunks, tor_strdup("\n"));
+    }
+  }
+
   /* Sort the votes. */
   smartlist_sort(votes, _compare_votes_by_authority_id);
   /* Add the authority sections. */

Modified: tor/trunk/src/or/eventdns.c
===================================================================
--- tor/trunk/src/or/eventdns.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/eventdns.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -2332,7 +2332,7 @@
 
 /* exported function */
 int
-evdns_nameserver_add(unsigned long int address) {
+evdns_nameserver_add(uint32_t address) {
 	struct sockaddr_in sin;
 	memset(&sin, 0, sizeof(sin));
 	sin.sin_family = AF_INET;
@@ -2363,13 +2363,13 @@
 
 	cp = strchr(ip_as_string, ':');
 	if (*ip_as_string == '[') {
-		int len;
+		size_t len;
 		if (!(cp = strchr(ip_as_string, ']'))) {
 			log(EVDNS_LOG_DEBUG, "Nameserver missing closing ]");
 			return 4;
 		}
 		len = cp-(ip_as_string + 1);
-		if (len > (int)sizeof(buf)-1) {
+		if (len > sizeof(buf)-1) {
 			log(EVDNS_LOG_DEBUG, "[Nameserver] does not fit in buffer.");
 			return 4;
 		}

Modified: tor/trunk/src/or/eventdns.h
===================================================================
--- tor/trunk/src/or/eventdns.h	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/eventdns.h	2009-09-25 17:06:41 UTC (rev 20669)
@@ -112,7 +112,7 @@
  *
  * API reference:
  *
- * int evdns_nameserver_add(unsigned long int address)
+ * int evdns_nameserver_add(uint32_t address)
  *	 Add a nameserver. The address should be an IP address in
  *	 network byte order. The type of address is chosen so that
  *	 it matches in_addr.s_addr.
@@ -258,7 +258,7 @@
 int evdns_init(void);
 void evdns_shutdown(int fail_requests);
 const char *evdns_err_to_string(int err);
-int evdns_nameserver_add(unsigned long int address);
+int evdns_nameserver_add(uint32_t address);
 int evdns_count_nameservers(void);
 int evdns_clear_nameservers_and_suspend(void);
 int evdns_resume(void);

Modified: tor/trunk/src/or/geoip.c
===================================================================
--- tor/trunk/src/or/geoip.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/geoip.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -12,8 +12,6 @@
 #include "ht.h"
 
 static void clear_geoip_db(void);
-static void dump_geoip_stats(void);
-static void dump_entry_stats(void);
 
 /** An entry from the GeoIP file: maps an IP range to a country. */
 typedef struct geoip_entry_t {
@@ -342,12 +340,11 @@
         ((double) (now - last_time_determined_shares));
     v3_share_times_seconds += v3_share *
         ((double) (now - last_time_determined_shares));
-    share_seconds += now - last_time_determined_shares;
+    share_seconds += (int)(now - last_time_determined_shares);
   }
   last_time_determined_shares = now;
 }
 
-#ifdef ENABLE_DIRREQ_STATS
 /** Calculate which fraction of v2 and v3 directory requests aimed at caches
  * have been sent to us since the last call of this function up to time
  * <b>now</b>. Set *<b>v2_share_out</b> and *<b>v3_share_out</b> to the
@@ -367,7 +364,25 @@
   share_seconds = 0;
   return 0;
 }
+
+/* Rotate period of v2 and v3 network status requests. */
+static void
+rotate_request_period(void)
+{
+  SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, {
+#if REQUEST_HIST_LEN > 1
+      memmove(&c->n_v2_ns_requests[0], &c->n_v2_ns_requests[1],
+              sizeof(uint32_t)*(REQUEST_HIST_LEN-1));
+      memmove(&c->n_v3_ns_requests[0], &c->n_v3_ns_requests[1],
+              sizeof(uint32_t)*(REQUEST_HIST_LEN-1));
 #endif
+      c->n_v2_ns_requests[REQUEST_HIST_LEN-1] = 0;
+      c->n_v3_ns_requests[REQUEST_HIST_LEN-1] = 0;
+    });
+  current_request_period_starts += REQUEST_HIST_PERIOD;
+  if (n_old_request_periods < REQUEST_HIST_LEN-1)
+    ++n_old_request_periods;
+}
 
 /** Note that we've seen a client connect from the IP <b>addr</b> (host order)
  * at time <b>now</b>. Ignored by all but bridges and directories if
@@ -379,55 +394,37 @@
   or_options_t *options = get_options();
   clientmap_entry_t lookup, *ent;
   if (action == GEOIP_CLIENT_CONNECT) {
-#ifdef ENABLE_ENTRY_STATS
-    if (!options->EntryStatistics)
+    /* Only remember statistics as entry guard or as bridge. */
+    if (!options->EntryStatistics &&
+        (!(options->BridgeRelay && options->BridgeRecordUsageByCountry)))
       return;
-#else
-    if (!(options->BridgeRelay && options->BridgeRecordUsageByCountry))
-      return;
-#endif
     /* Did we recently switch from bridge to relay or back? */
     if (client_history_starts > now)
       return;
   } else {
-#ifndef ENABLE_DIRREQ_STATS
-    return;
-#else
     if (options->BridgeRelay || options->BridgeAuthoritativeDir ||
         !options->DirReqStatistics)
       return;
-#endif
   }
 
-  /* Rotate the current request period. */
-  while (current_request_period_starts + REQUEST_HIST_PERIOD < now) {
-    if (!geoip_countries)
-      geoip_countries = smartlist_create();
-    if (!current_request_period_starts) {
-      current_request_period_starts = now;
-      break;
+  /* As a bridge that doesn't rotate request periods every 24 hours,
+   * possibly rotate now. */
+  if (options->BridgeRelay) {
+    while (current_request_period_starts + REQUEST_HIST_PERIOD < now) {
+      if (!geoip_countries)
+        geoip_countries = smartlist_create();
+      if (!current_request_period_starts) {
+        current_request_period_starts = now;
+        break;
+      }
+      /* Also discard all items in the client history that are too old.
+       * (This only works here because bridge and directory stats are
+       * independent. Otherwise, we'd only want to discard those items
+       * with action GEOIP_CLIENT_NETWORKSTATUS{_V2}.) */
+      geoip_remove_old_clients(current_request_period_starts);
+      /* Now rotate request period */
+      rotate_request_period();
     }
-    /* Also discard all items in the client history that are too old.
-     * (This only works here because bridge and directory stats are
-     * independent. Otherwise, we'd only want to discard those items
-     * with action GEOIP_CLIENT_NETWORKSTATUS{_V2}.) */
-    geoip_remove_old_clients(current_request_period_starts);
-    /* Before rotating, write the current stats to disk. */
-    dump_geoip_stats();
-    if (get_options()->EntryStatistics)
-      dump_entry_stats();
-    /* Now rotate request period */
-    SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, {
-        memmove(&c->n_v2_ns_requests[0], &c->n_v2_ns_requests[1],
-                sizeof(uint32_t)*(REQUEST_HIST_LEN-1));
-        memmove(&c->n_v3_ns_requests[0], &c->n_v3_ns_requests[1],
-                sizeof(uint32_t)*(REQUEST_HIST_LEN-1));
-        c->n_v2_ns_requests[REQUEST_HIST_LEN-1] = 0;
-        c->n_v3_ns_requests[REQUEST_HIST_LEN-1] = 0;
-      });
-    current_request_period_starts += REQUEST_HIST_PERIOD;
-    if (n_old_request_periods < REQUEST_HIST_LEN-1)
-      ++n_old_request_periods;
   }
 
   lookup.ipaddr = addr;
@@ -495,7 +492,6 @@
     client_history_starts = cutoff;
 }
 
-#ifdef ENABLE_DIRREQ_STATS
 /** How many responses are we giving to clients requesting v2 network
  * statuses? */
 static uint32_t ns_v2_responses[GEOIP_NS_RESPONSE_NUM];
@@ -503,7 +499,6 @@
 /** How many responses are we giving to clients requesting v3 network
  * statuses? */
 static uint32_t ns_v3_responses[GEOIP_NS_RESPONSE_NUM];
-#endif
 
 /** Note that we've rejected a client's request for a v2 or v3 network
  * status, encoded in <b>action</b> for reason <b>reason</b> at time
@@ -512,7 +507,6 @@
 geoip_note_ns_response(geoip_client_action_t action,
                        geoip_ns_response_t response)
 {
-#ifdef ENABLE_DIRREQ_STATS
   static int arrays_initialized = 0;
   if (!get_options()->DirReqStatistics)
     return;
@@ -528,10 +522,6 @@
     ns_v3_responses[response]++;
   else
     ns_v2_responses[response]++;
-#else
-  (void) action;
-  (void) response;
-#endif
 }
 
 /** Do not mention any country from which fewer than this number of IPs have
@@ -709,7 +699,6 @@
   }
 }
 
-#ifdef ENABLE_DIRREQ_STATS
 /** Return a newly allocated comma-separated string containing statistics
  * on network status downloads. The string contains the number of completed
  * requests, timeouts, and still running requests as well as the download
@@ -720,9 +709,9 @@
                            dirreq_type_t type)
 {
   char *result = NULL;
-  smartlist_t *dirreq_times = NULL;
+  smartlist_t *dirreq_completed = NULL;
   uint32_t complete = 0, timeouts = 0, running = 0;
-  int i = 0, bufsize = 1024, written;
+  int bufsize = 1024, written;
   dirreq_map_entry_t **ptr, **next, *ent;
   struct timeval now;
 
@@ -730,7 +719,7 @@
   if (action != GEOIP_CLIENT_NETWORKSTATUS &&
       action != GEOIP_CLIENT_NETWORKSTATUS_V2)
     return NULL;
-  dirreq_times = smartlist_create();
+  dirreq_completed = smartlist_create();
   for (ptr = HT_START(dirreqmap, &dirreq_map); ptr; ptr = next) {
     ent = *ptr;
     if (ent->action != action || ent->type != type) {
@@ -738,23 +727,17 @@
       continue;
     } else {
       if (ent->completed) {
-        uint32_t *bytes_per_second = tor_malloc_zero(sizeof(uint32_t));
-        uint32_t time_diff = (uint32_t) tv_mdiff(&ent->request_time,
-                                                 &ent->completion_time);
-        if (time_diff == 0)
-          time_diff = 1; /* Avoid DIV/0; "instant" answers are impossible
-                          * anyway by law of nature or something.. */
-        *bytes_per_second = 1000 * ent->response_size / time_diff;
-        smartlist_add(dirreq_times, bytes_per_second);
+        smartlist_add(dirreq_completed, ent);
         complete++;
+        next = HT_NEXT_RMV(dirreqmap, &dirreq_map, ptr);
       } else {
         if (tv_mdiff(&ent->request_time, &now) / 1000 > DIRREQ_TIMEOUT)
           timeouts++;
         else
           running++;
+        next = HT_NEXT_RMV(dirreqmap, &dirreq_map, ptr);
+        tor_free(ent);
       }
-      next = HT_NEXT_RMV(dirreqmap, &dirreq_map, ptr);
-      tor_free(ent);
     }
   }
 #define DIR_REQ_GRANULARITY 4
@@ -767,16 +750,30 @@
   result = tor_malloc_zero(bufsize);
   written = tor_snprintf(result, bufsize, "complete=%u,timeout=%u,"
                          "running=%u", complete, timeouts, running);
-  if (written < 0)
-    return NULL;
+  if (written < 0) {
+    tor_free(result);
+    goto done;
+  }
+
 #define MIN_DIR_REQ_RESPONSES 16
   if (complete >= MIN_DIR_REQ_RESPONSES) {
-    uint32_t *dltimes = tor_malloc(sizeof(uint32_t) * complete);
-    SMARTLIST_FOREACH(dirreq_times, uint32_t *, dlt, {
-      dltimes[i++] = *dlt;
-      tor_free(dlt);
-    });
-    median_uint32(dltimes, complete); /* sort */
+    uint32_t *dltimes;
+    /* We may have rounded 'completed' up.  Here we want to use the
+     * real value. */
+    complete = smartlist_len(dirreq_completed);
+    dltimes = tor_malloc_zero(sizeof(uint32_t) * complete);
+    SMARTLIST_FOREACH_BEGIN(dirreq_completed, dirreq_map_entry_t *, ent) {
+      uint32_t bytes_per_second;
+      uint32_t time_diff = (uint32_t) tv_mdiff(&ent->request_time,
+                                               &ent->completion_time);
+      if (time_diff == 0)
+        time_diff = 1; /* Avoid DIV/0; "instant" answers are impossible
+                        * by law of nature or something, but a milisecond
+                        * is a bit greater than "instantly" */
+      bytes_per_second = (uint32_t)(1000 * ent->response_size / time_diff);
+      dltimes[ent_sl_idx] = bytes_per_second;
+    } SMARTLIST_FOREACH_END(ent);
+    median_uint32(dltimes, complete); /* sorts as a side effect. */
     written = tor_snprintf(result + written, bufsize - written,
                            ",min=%u,d1=%u,d2=%u,q1=%u,d3=%u,d4=%u,md=%u,"
                            "d6=%u,d7=%u,q3=%u,d8=%u,d9=%u,max=%u",
@@ -793,32 +790,28 @@
                            dltimes[8*complete/10-1],
                            dltimes[9*complete/10-1],
                            dltimes[complete-1]);
+    if (written<0)
+      tor_free(result);
     tor_free(dltimes);
   }
-  if (written < 0)
-    result = NULL;
-  smartlist_free(dirreq_times);
+ done:
+  SMARTLIST_FOREACH(dirreq_completed, dirreq_map_entry_t *, ent,
+                    tor_free(ent));
+  smartlist_free(dirreq_completed);
   return result;
 }
-#endif
 
 /** How long do we have to have observed per-country request history before we
  * are willing to talk about it? */
 #define GEOIP_MIN_OBSERVATION_TIME (12*60*60)
 
-/** Return a newly allocated comma-separated string containing entries for all
- * the countries from which we've seen enough clients connect. The entry
- * format is cc=num where num is the number of IPs we've seen connecting from
- * that country, and cc is a lowercased country code. Returns NULL if we don't
- * want to export geoip data yet. */
-char *
-geoip_get_client_history(time_t now, geoip_client_action_t action)
+/** Helper for geoip_get_client_history_dirreq() and
+ * geoip_get_client_history_bridge(). */
+static char *
+geoip_get_client_history(time_t now, geoip_client_action_t action,
+                         int min_observation_time, unsigned granularity)
 {
   char *result = NULL;
-  int min_observation_time = GEOIP_MIN_OBSERVATION_TIME;
-#ifdef ENABLE_DIRREQ_STATS
-  min_observation_time = DIR_RECORD_USAGE_MIN_OBSERVATION_TIME;
-#endif
   if (!geoip_is_loaded())
     return NULL;
   if (client_history_starts < (now - min_observation_time)) {
@@ -830,10 +823,6 @@
     clientmap_entry_t **ent;
     unsigned *counts = tor_malloc_zero(sizeof(unsigned)*n_countries);
     unsigned total = 0;
-    unsigned granularity = IP_GRANULARITY;
-#ifdef ENABLE_DIRREQ_STATS
-    granularity = DIR_RECORD_USAGE_GRANULARITY;
-#endif
     HT_FOREACH(ent, clientmap, &client_history) {
       int country;
       if ((*ent)->action != (int)action)
@@ -889,6 +878,34 @@
   return result;
 }
 
+/** Return a newly allocated comma-separated string containing entries for
+ * all the countries from which we've seen enough clients connect as a
+ * directory. The entry format is cc=num where num is the number of IPs
+ * we've seen connecting from that country, and cc is a lowercased country
+ * code. Returns NULL if we don't want to export geoip data yet. */
+char *
+geoip_get_client_history_dirreq(time_t now,
+                                geoip_client_action_t action)
+{
+  return geoip_get_client_history(now, action,
+                                  DIR_RECORD_USAGE_MIN_OBSERVATION_TIME,
+                                  DIR_RECORD_USAGE_GRANULARITY);
+}
+
+/** Return a newly allocated comma-separated string containing entries for
+ * all the countries from which we've seen enough clients connect as a
+ * bridge. The entry format is cc=num where num is the number of IPs
+ * we've seen connecting from that country, and cc is a lowercased country
+ * code. Returns NULL if we don't want to export geoip data yet. */
+char *
+geoip_get_client_history_bridge(time_t now,
+                                geoip_client_action_t action)
+{
+  return geoip_get_client_history(now, action,
+                                  GEOIP_MIN_OBSERVATION_TIME,
+                                  IP_GRANULARITY);
+}
+
 /** Return a newly allocated string holding the per-country request history
  * for <b>action</b> in a format suitable for an extra-info document, or NULL
  * on failure. */
@@ -899,10 +916,6 @@
   char *result;
   unsigned granularity = IP_GRANULARITY;
   int min_observation_time = GEOIP_MIN_OBSERVATION_TIME;
-#ifdef ENABLE_DIRREQ_STATS
-  granularity = DIR_RECORD_USAGE_GRANULARITY;
-  min_observation_time = DIR_RECORD_USAGE_MIN_OBSERVATION_TIME;
-#endif
 
   if (client_history_starts >= (now - min_observation_time))
     return NULL;
@@ -944,16 +957,23 @@
   return result;
 }
 
-/** Store all our geoip statistics into $DATADIR/dirreq-stats. */
-static void
-dump_geoip_stats(void)
+/** Start time of directory request stats. */
+static time_t start_of_dirreq_stats_interval;
+
+/** Initialize directory request stats. */
+void
+geoip_dirreq_stats_init(time_t now)
 {
-#ifdef ENABLE_DIRREQ_STATS
-  time_t now = time(NULL);
-  time_t request_start;
-  char *filename = get_datadir_fname("dirreq-stats");
+  start_of_dirreq_stats_interval = now;
+}
+
+/** Write dirreq statistics to $DATADIR/stats/dirreq-stats. */
+void
+geoip_dirreq_stats_write(time_t now)
+{
+  char *statsdir = NULL, *filename = NULL;
   char *data_v2 = NULL, *data_v3 = NULL;
-  char since[ISO_TIME_LEN+1], written[ISO_TIME_LEN+1];
+  char written[ISO_TIME_LEN+1];
   open_file_t *open_file = NULL;
   double v2_share = 0.0, v3_share = 0.0;
   FILE *out;
@@ -962,30 +982,37 @@
   if (!get_options()->DirReqStatistics)
     goto done;
 
-  data_v2 = geoip_get_client_history(now, GEOIP_CLIENT_NETWORKSTATUS_V2);
-  data_v3 = geoip_get_client_history(now, GEOIP_CLIENT_NETWORKSTATUS);
-  format_iso_time(since, geoip_get_history_start());
+  /* Discard all items in the client history that are too old. */
+  geoip_remove_old_clients(start_of_dirreq_stats_interval);
+
+  statsdir = get_datadir_fname("stats");
+  if (check_private_dir(statsdir, CPD_CREATE) < 0)
+    goto done;
+  filename = get_datadir_fname("stats"PATH_SEPARATOR"dirreq-stats");
+  data_v2 = geoip_get_client_history_dirreq(now,
+                GEOIP_CLIENT_NETWORKSTATUS_V2);
+  data_v3 = geoip_get_client_history_dirreq(now,
+                GEOIP_CLIENT_NETWORKSTATUS);
   format_iso_time(written, now);
   out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND,
                                     0600, &open_file);
   if (!out)
     goto done;
-  if (fprintf(out, "written %s\nstarted-at %s\nns-ips %s\nns-v2-ips %s\n",
-              written, since,
+  if (fprintf(out, "dirreq-stats-end %s (%d s)\ndirreq-v3-ips %s\n"
+              "dirreq-v2-ips %s\n", written,
+              (unsigned) (now - start_of_dirreq_stats_interval),
               data_v3 ? data_v3 : "", data_v2 ? data_v2 : "") < 0)
     goto done;
   tor_free(data_v2);
   tor_free(data_v3);
 
-  request_start = current_request_period_starts -
-    (n_old_request_periods * REQUEST_HIST_PERIOD);
-  format_iso_time(since, request_start);
   data_v2 = geoip_get_request_history(now, GEOIP_CLIENT_NETWORKSTATUS_V2);
   data_v3 = geoip_get_request_history(now, GEOIP_CLIENT_NETWORKSTATUS);
-  if (fprintf(out, "requests-start %s\nn-ns-reqs %s\nn-v2-ns-reqs %s\n",
-              since,
+  if (fprintf(out, "dirreq-v3-reqs %s\ndirreq-v2-reqs %s\n",
               data_v3 ? data_v3 : "", data_v2 ? data_v2 : "") < 0)
     goto done;
+  tor_free(data_v2);
+  tor_free(data_v3);
 #define RESPONSE_GRANULARITY 8
   for (i = 0; i < GEOIP_NS_RESPONSE_NUM; i++) {
     ns_v2_responses[i] = round_uint32_to_next_multiple_of(
@@ -994,7 +1021,7 @@
                                ns_v3_responses[i], RESPONSE_GRANULARITY);
   }
 #undef RESPONSE_GRANULARITY
-  if (fprintf(out, "n-ns-resp ok=%u,not-enough-sigs=%u,unavailable=%u,"
+  if (fprintf(out, "dirreq-v3-resp ok=%u,not-enough-sigs=%u,unavailable=%u,"
                    "not-found=%u,not-modified=%u,busy=%u\n",
                    ns_v3_responses[GEOIP_SUCCESS],
                    ns_v3_responses[GEOIP_REJECT_NOT_ENOUGH_SIGS],
@@ -1003,7 +1030,7 @@
                    ns_v3_responses[GEOIP_REJECT_NOT_MODIFIED],
                    ns_v3_responses[GEOIP_REJECT_BUSY]) < 0)
     goto done;
-  if (fprintf(out, "n-v2-ns-resp ok=%u,unavailable=%u,"
+  if (fprintf(out, "dirreq-v2-resp ok=%u,unavailable=%u,"
                    "not-found=%u,not-modified=%u,busy=%u\n",
                    ns_v2_responses[GEOIP_SUCCESS],
                    ns_v2_responses[GEOIP_REJECT_UNAVAILABLE],
@@ -1014,9 +1041,9 @@
   memset(ns_v2_responses, 0, sizeof(ns_v2_responses));
   memset(ns_v3_responses, 0, sizeof(ns_v3_responses));
   if (!geoip_get_mean_shares(now, &v2_share, &v3_share)) {
-    if (fprintf(out, "v2-ns-share %0.2lf%%\n", v2_share*100) < 0)
+    if (fprintf(out, "dirreq-v2-share %0.2lf%%\n", v2_share*100) < 0)
       goto done;
-    if (fprintf(out, "v3-ns-share %0.2lf%%\n", v3_share*100) < 0)
+    if (fprintf(out, "dirreq-v3-share %0.2lf%%\n", v3_share*100) < 0)
       goto done;
   }
 
@@ -1024,7 +1051,7 @@
                                        DIRREQ_DIRECT);
   data_v3 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS,
                                        DIRREQ_DIRECT);
-  if (fprintf(out, "ns-direct-dl %s\nns-v2-direct-dl %s\n",
+  if (fprintf(out, "dirreq-v3-direct-dl %s\ndirreq-v2-direct-dl %s\n",
               data_v3 ? data_v3 : "", data_v2 ? data_v2 : "") < 0)
     goto done;
   tor_free(data_v2);
@@ -1033,53 +1060,78 @@
                                        DIRREQ_TUNNELED);
   data_v3 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS,
                                        DIRREQ_TUNNELED);
-  if (fprintf(out, "ns-tunneled-dl %s\nns-v2-tunneled-dl %s\n",
+  if (fprintf(out, "dirreq-v3-tunneled-dl %s\ndirreq-v2-tunneled-dl %s\n",
               data_v3 ? data_v3 : "", data_v2 ? data_v2 : "") < 0)
     goto done;
 
   finish_writing_to_file(open_file);
   open_file = NULL;
+
+  /* Rotate request period */
+  rotate_request_period();
+
+  start_of_dirreq_stats_interval = now;
+
  done:
   if (open_file)
     abort_writing_to_file(open_file);
   tor_free(filename);
+  tor_free(statsdir);
   tor_free(data_v2);
   tor_free(data_v3);
-#endif
 }
 
-/** Store all our geoip statistics as entry guards into
- * $DATADIR/entry-stats. */
-static void
-dump_entry_stats(void)
+/** Start time of entry stats. */
+static time_t start_of_entry_stats_interval;
+
+/** Initialize entry stats. */
+void
+geoip_entry_stats_init(time_t now)
 {
-#ifdef ENABLE_ENTRY_STATS
-  time_t now = time(NULL);
-  char *filename = get_datadir_fname("entry-stats");
+  start_of_entry_stats_interval = now;
+}
+
+/** Write entry statistics to $DATADIR/stats/entry-stats. */
+void
+geoip_entry_stats_write(time_t now)
+{
+  char *statsdir = NULL, *filename = NULL;
   char *data = NULL;
-  char since[ISO_TIME_LEN+1], written[ISO_TIME_LEN+1];
+  char written[ISO_TIME_LEN+1];
   open_file_t *open_file = NULL;
   FILE *out;
 
-  data = geoip_get_client_history(now, GEOIP_CLIENT_CONNECT);
-  format_iso_time(since, geoip_get_history_start());
+  if (!get_options()->EntryStatistics)
+    goto done;
+
+  /* Discard all items in the client history that are too old. */
+  geoip_remove_old_clients(start_of_entry_stats_interval);
+
+  statsdir = get_datadir_fname("stats");
+  if (check_private_dir(statsdir, CPD_CREATE) < 0)
+    goto done;
+  filename = get_datadir_fname("stats"PATH_SEPARATOR"entry-stats");
+  data = geoip_get_client_history_dirreq(now, GEOIP_CLIENT_CONNECT);
   format_iso_time(written, now);
   out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND,
                                     0600, &open_file);
   if (!out)
     goto done;
-  if (fprintf(out, "written %s\nstarted-at %s\nips %s\n",
-              written, since, data ? data : "") < 0)
+  if (fprintf(out, "entry-stats-end %s (%u s)\nentry-ips %s\n",
+              written, (unsigned) (now - start_of_entry_stats_interval),
+              data ? data : "") < 0)
     goto done;
 
+  start_of_entry_stats_interval = now;
+
   finish_writing_to_file(open_file);
   open_file = NULL;
  done:
   if (open_file)
     abort_writing_to_file(open_file);
   tor_free(filename);
+  tor_free(statsdir);
   tor_free(data);
-#endif
 }
 
 /** Helper used to implement GETINFO ip-to-country/... controller command. */

Modified: tor/trunk/src/or/main.c
===================================================================
--- tor/trunk/src/or/main.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/main.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -830,9 +830,7 @@
   static time_t time_to_clean_caches = 0;
   static time_t time_to_recheck_bandwidth = 0;
   static time_t time_to_check_for_expired_networkstatus = 0;
-#ifdef ENABLE_BUFFER_STATS
-  static time_t time_to_dump_buffer_stats = 0;
-#endif
+  static time_t time_to_write_stats_files = 0;
   static time_t time_to_retry_dns_init = 0;
   or_options_t *options = get_options();
   int i;
@@ -960,13 +958,44 @@
     time_to_check_for_expired_networkstatus = now + CHECK_EXPIRED_NS_INTERVAL;
   }
 
-#ifdef ENABLE_BUFFER_STATS
-  if (time_to_dump_buffer_stats < now) {
-    if (get_options()->CellStatistics && time_to_dump_buffer_stats)
-      dump_buffer_stats();
-    time_to_dump_buffer_stats = now + DUMP_BUFFER_STATS_INTERVAL;
+  /* 1g. Check whether we should write statistics to disk.
+   */
+  if (time_to_write_stats_files >= 0 && time_to_write_stats_files < now) {
+#define WRITE_STATS_INTERVAL (24*60*60)
+    if (options->CellStatistics || options->DirReqStatistics ||
+        options->EntryStatistics || options->ExitPortStatistics) {
+      if (!time_to_write_stats_files) {
+        /* Initialize stats. */
+        if (options->CellStatistics)
+          rep_hist_buffer_stats_init(now);
+        if (options->DirReqStatistics)
+          geoip_dirreq_stats_init(now);
+        if (options->EntryStatistics)
+          geoip_entry_stats_init(now);
+        if (options->ExitPortStatistics)
+          rep_hist_exit_stats_init(now);
+        log_notice(LD_CONFIG, "Configured to measure statistics. Look for "
+                   "the *-stats files that will first be written to the "
+                   "data directory in %d hours from now.",
+                   WRITE_STATS_INTERVAL / (60 * 60));
+        time_to_write_stats_files = now + WRITE_STATS_INTERVAL;
+      } else {
+        /* Write stats to disk. */
+        if (options->CellStatistics)
+          rep_hist_buffer_stats_write(time_to_write_stats_files);
+        if (options->DirReqStatistics)
+          geoip_dirreq_stats_write(time_to_write_stats_files);
+        if (options->EntryStatistics)
+          geoip_entry_stats_write(time_to_write_stats_files);
+        if (options->ExitPortStatistics)
+          rep_hist_exit_stats_write(time_to_write_stats_files);
+        time_to_write_stats_files += WRITE_STATS_INTERVAL;
+      }
+    } else {
+      /* Never write stats to disk */
+      time_to_write_stats_files = -1;
+    }
   }
-#endif
 
   /* Remove old information from rephist and the rend cache. */
   if (time_to_clean_caches < now) {
@@ -1211,17 +1240,26 @@
         TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
     /* every 20 minutes, check and complain if necessary */
     routerinfo_t *me = router_get_my_routerinfo();
-    if (me && !check_whether_orport_reachable())
+    if (me && !check_whether_orport_reachable()) {
       log_warn(LD_CONFIG,"Your server (%s:%d) has not managed to confirm that "
                "its ORPort is reachable. Please check your firewalls, ports, "
                "address, /etc/hosts file, etc.",
                me->address, me->or_port);
-    if (me && !check_whether_dirport_reachable())
+      control_event_server_status(LOG_WARN,
+                                  "REACHABILITY_FAILED ORADDRESS=%s:%d",
+                                  me->address, me->or_port);
+    }
+
+    if (me && !check_whether_dirport_reachable()) {
       log_warn(LD_CONFIG,
                "Your server (%s:%d) has not managed to confirm that its "
                "DirPort is reachable. Please check your firewalls, ports, "
                "address, /etc/hosts file, etc.",
                me->address, me->dir_port);
+      control_event_server_status(LOG_WARN,
+                                  "REACHABILITY_FAILED DIRADDRESS=%s:%d",
+                                  me->address, me->dir_port);
+    }
   }
 
 /** If more than this many seconds have elapsed, probably the clock
@@ -1410,8 +1448,10 @@
   /* initialize the bootstrap status events to know we're starting up */
   control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0);
 
-  if (trusted_dirs_reload_certs())
-    return -1;
+  if (trusted_dirs_reload_certs()) {
+    log_warn(LD_DIR,
+             "Couldn't load all cached v3 certificates. Starting anyway.");
+  }
   if (router_reload_v2_networkstatus()) {
     return -1;
   }
@@ -1622,7 +1662,7 @@
 {
   time_t now = time(NULL);
   time_t elapsed;
-  int rbuf_cap, wbuf_cap, rbuf_len, wbuf_len;
+  size_t rbuf_cap, wbuf_cap, rbuf_len, wbuf_len;
 
   log(severity, LD_GENERAL, "Dumping stats:");
 
@@ -1658,7 +1698,7 @@
           log(severity, LD_GENERAL,
               "Conn %d: %d/%d bytes used on OpenSSL read buffer; "
               "%d/%d bytes used on write buffer.",
-              i, rbuf_len, rbuf_cap, wbuf_len, wbuf_cap);
+              i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap);
         }
       }
     }

Modified: tor/trunk/src/or/networkstatus.c
===================================================================
--- tor/trunk/src/or/networkstatus.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/networkstatus.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -286,6 +286,10 @@
     SMARTLIST_FOREACH(ns->known_flags, char *, c, tor_free(c));
     smartlist_free(ns->known_flags);
   }
+  if (ns->net_params) {
+    SMARTLIST_FOREACH(ns->net_params, char *, c, tor_free(c));
+    smartlist_free(ns->net_params);
+  }
   if (ns->supported_methods) {
     SMARTLIST_FOREACH(ns->supported_methods, char *, c, tor_free(c));
     smartlist_free(ns->supported_methods);
@@ -1889,6 +1893,37 @@
   tor_free(status);
 }
 
+/** Return the value of a integer parameter from the networkstatus <b>ns</b>
+ * whose name is <b>param_name</b>.  If <b>ns</b> is NULL, try loading the
+ * latest consensus ourselves. Return <b>default_val</b> if no latest
+ * consensus, or if it has no parameter called <b>param_name</b>. */
+int32_t
+networkstatus_get_param(networkstatus_t *ns, const char *param_name,
+                        int32_t default_val)
+{
+  size_t name_len;
+
+  if (!ns) /* if they pass in null, go find it ourselves */
+    ns = networkstatus_get_latest_consensus();
+
+  if (!ns || !ns->net_params)
+    return default_val;
+
+  name_len = strlen(param_name);
+
+  SMARTLIST_FOREACH_BEGIN(ns->net_params, const char *, p) {
+    if (!strcmpstart(p, param_name) && p[name_len] == '=') {
+      int ok=0;
+      long v = tor_parse_long(p+name_len+1, 10, INT32_MIN,
+                              INT32_MAX, &ok, NULL);
+      if (ok)
+        return (int32_t) v;
+    }
+  } SMARTLIST_FOREACH_END(p);
+
+  return default_val;
+}
+
 /** If <b>question</b> is a string beginning with "ns/" in a format the
  * control interface expects for a GETINFO question, set *<b>answer</b> to a
  * newly-allocated string containing networkstatus lines for the appropriate

Modified: tor/trunk/src/or/or.h
===================================================================
--- tor/trunk/src/or/or.h	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/or.h	2009-09-25 17:06:41 UTC (rev 20669)
@@ -20,13 +20,7 @@
 #ifndef INSTRUMENT_DOWNLOADS
 #define INSTRUMENT_DOWNLOADS 1
 #endif
-#ifndef ENABLE_DIRREQ_STATS
-#define ENABLE_DIRREQ_STATS 1
 #endif
-#ifndef ENABLE_BUFFER_STATS
-#define ENABLE_BUFFER_STATS 1
-#endif
-#endif
 
 #ifdef MS_WINDOWS
 #define WIN32_WINNT 0x400
@@ -223,6 +217,21 @@
 /* !!!! If _CONN_TYPE_MAX is ever over 15, we must grow the type field in
  * connection_t. */
 
+/* Proxy client types */
+#define PROXY_NONE 0
+#define PROXY_CONNECT 1
+#define PROXY_SOCKS4 2
+#define PROXY_SOCKS5 3
+
+/* Proxy client handshake states */
+#define PROXY_HTTPS_WANT_CONNECT_OK 1
+#define PROXY_SOCKS4_WANT_CONNECT_OK 2
+#define PROXY_SOCKS5_WANT_AUTH_METHOD_NONE 3
+#define PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929 4
+#define PROXY_SOCKS5_WANT_AUTH_RFC1929_OK 5
+#define PROXY_SOCKS5_WANT_CONNECT_OK 6
+#define PROXY_CONNECTED 7
+
 /** True iff <b>x</b> is an edge connection. */
 #define CONN_IS_EDGE(x) \
   ((x)->type == CONN_TYPE_EXIT || (x)->type == CONN_TYPE_AP)
@@ -243,26 +252,24 @@
 #define _OR_CONN_STATE_MIN 1
 /** State for a connection to an OR: waiting for connect() to finish. */
 #define OR_CONN_STATE_CONNECTING 1
-/** State for a connection to an OR: waiting for proxy command to flush. */
-#define OR_CONN_STATE_PROXY_FLUSHING 2
-/** State for a connection to an OR: waiting for proxy response. */
-#define OR_CONN_STATE_PROXY_READING 3
+/** State for a connection to an OR: waiting for proxy handshake to complete */
+#define OR_CONN_STATE_PROXY_HANDSHAKING 2
 /** State for a connection to an OR or client: SSL is handshaking, not done
  * yet. */
-#define OR_CONN_STATE_TLS_HANDSHAKING 4
+#define OR_CONN_STATE_TLS_HANDSHAKING 3
 /** State for a connection to an OR: We're doing a second SSL handshake for
  * renegotiation purposes. */
-#define OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING 5
+#define OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING 4
 /** State for a connection at an OR: We're waiting for the client to
  * renegotiate. */
-#define OR_CONN_STATE_TLS_SERVER_RENEGOTIATING 6
+#define OR_CONN_STATE_TLS_SERVER_RENEGOTIATING 5
 /** State for a connection to an OR: We're done with our SSL handshake, but we
  * haven't yet negotiated link protocol versions and sent a netinfo cell.
  */
-#define OR_CONN_STATE_OR_HANDSHAKING 7
+#define OR_CONN_STATE_OR_HANDSHAKING 6
 /** State for a connection to an OR: Ready to send/receive cells. */
-#define OR_CONN_STATE_OPEN 8
-#define _OR_CONN_STATE_MAX 8
+#define OR_CONN_STATE_OPEN 7
+#define _OR_CONN_STATE_MAX 7
 
 #define _EXIT_CONN_STATE_MIN 1
 /** State for an exit connection: waiting for response from DNS farm. */
@@ -841,17 +848,30 @@
 typedef struct packed_cell_t {
   struct packed_cell_t *next; /**< Next cell queued on this circuit. */
   char body[CELL_NETWORK_SIZE]; /**< Cell as packed for network. */
-#ifdef ENABLE_BUFFER_STATS
-  struct timeval packed_timeval; /**< When was this cell packed? */
-#endif
 } packed_cell_t;
 
+/** Number of cells added to a circuit queue including their insertion
+ * time on 10 millisecond detail; used for buffer statistics. */
+typedef struct insertion_time_elem_t {
+  struct insertion_time_elem_t *next; /**< Next element in queue. */
+  uint32_t insertion_time; /**< When were cells inserted (in 10 ms steps
+                             * starting at 0:00 of the current day)? */
+  unsigned counter; /**< How many cells were inserted? */
+} insertion_time_elem_t;
+
+/** Queue of insertion times. */
+typedef struct insertion_time_queue_t {
+  struct insertion_time_elem_t *first; /**< First element in queue. */
+  struct insertion_time_elem_t *last; /**< Last element in queue. */
+} insertion_time_queue_t;
+
 /** A queue of cells on a circuit, waiting to be added to the
  * or_connection_t's outbuf. */
 typedef struct cell_queue_t {
   packed_cell_t *head; /**< The first cell, or NULL if the queue is empty. */
   packed_cell_t *tail; /**< The last cell, or NULL if the queue is empty. */
   int n; /**< The number of cells in the queue. */
+  insertion_time_queue_t *insertion_times; /**< Insertion times of cells. */
 } cell_queue_t;
 
 /** Beginning of a RELAY cell payload. */
@@ -932,6 +952,9 @@
    * connection. */
   unsigned int linked_conn_is_closed:1;
 
+  /** CONNECT/SOCKS proxy client handshake state (for outgoing connections). */
+  unsigned int proxy_state:4;
+
   /** 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. */
@@ -975,10 +998,8 @@
    * to the evdns_server_port is uses to listen to and answer connections. */
   struct evdns_server_port *dns_server_port;
 
-#ifdef ENABLE_DIRREQ_STATS
   /** Unique ID for measuring tunneled network status requests. */
   uint64_t dirreq_id;
-#endif
 } connection_t;
 
 /** Stores flags and information related to the portion of a v2 Tor OR
@@ -1651,6 +1672,10 @@
    * not listed here, the voter has no opinion on what its value should be. */
   smartlist_t *known_flags;
 
+  /** List of key=value strings for the parameters in this vote or
+   * consensus, sorted by key. */
+  smartlist_t *net_params;
+
   /** List of networkstatus_voter_info_t.  For a vote, only one element
    * is included.  For a consensus, one element is included for every voter
    * whose vote contributed to the consensus. */
@@ -1845,9 +1870,9 @@
   struct crypt_path_t *prev; /**< Link to previous crypt_path_t in the
                               * circuit. */
 
-  int package_window; /**< How many bytes are we allowed to originate ending
+  int package_window; /**< How many cells are we allowed to originate ending
                        * at this step? */
-  int deliver_window; /**< How many bytes are we willing to deliver originating
+  int deliver_window; /**< How many cells are we willing to deliver originating
                        * at this step? */
 } crypt_path_t;
 
@@ -1952,6 +1977,7 @@
   time_t timestamp_created; /**< When was this circuit 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
@@ -1968,10 +1994,9 @@
    * linked to an OR connection. */
   struct circuit_t *prev_active_on_n_conn;
   struct circuit_t *next; /**< Next circuit in linked list of all circuits. */
-#ifdef ENABLE_DIRREQ_STATS
+
   /** Unique ID for measuring tunneled network status requests. */
   uint64_t dirreq_id;
-#endif
 } circuit_t;
 
 /** Largest number of relay_early cells that we can send on a given
@@ -2095,7 +2120,6 @@
   /** True iff this circuit was made with a CREATE_FAST cell. */
   unsigned int is_first_hop : 1;
 
-#ifdef ENABLE_BUFFER_STATS
   /** Number of cells that were removed from circuit queue; reset every
    * time when writing buffer stats to disk. */
   uint32_t processed_cells;
@@ -2104,7 +2128,6 @@
    * exit-ward queues of this circuit; reset every time when writing
    * buffer stats to disk. */
   uint64_t total_cell_waiting_time;
-#endif
 } or_circuit_t;
 
 /** Convert a circuit subtype to a circuit_t.*/
@@ -2366,15 +2389,25 @@
   char *ContactInfo; /**< Contact info to be published in the directory. */
 
   char *HttpProxy; /**< hostname[:port] to use as http proxy, if any. */
-  uint32_t HttpProxyAddr; /**< Parsed IPv4 addr for http proxy, if any. */
+  tor_addr_t HttpProxyAddr; /**< Parsed IPv4 addr for http proxy, if any. */
   uint16_t HttpProxyPort; /**< Parsed port for http proxy, if any. */
   char *HttpProxyAuthenticator; /**< username:password string, if any. */
 
   char *HttpsProxy; /**< hostname[:port] to use as https proxy, if any. */
-  uint32_t HttpsProxyAddr; /**< Parsed IPv4 addr for https proxy, if any. */
+  tor_addr_t HttpsProxyAddr; /**< Parsed addr for https proxy, if any. */
   uint16_t HttpsProxyPort; /**< Parsed port for https proxy, if any. */
   char *HttpsProxyAuthenticator; /**< username:password string, if any. */
 
+  char *Socks4Proxy; /**< hostname:port to use as a SOCKS4 proxy, if any. */
+  tor_addr_t Socks4ProxyAddr; /**< Derived from Socks4Proxy. */
+  uint16_t Socks4ProxyPort; /**< Derived from Socks4Proxy. */
+
+  char *Socks5Proxy; /**< hostname:port to use as a SOCKS5 proxy, if any. */
+  tor_addr_t Socks5ProxyAddr; /**< Derived from Sock5Proxy. */
+  uint16_t Socks5ProxyPort; /**< Derived from Socks5Proxy. */
+  char *Socks5ProxyUsername; /**< Username for SOCKS5 authentication, if any */
+  char *Socks5ProxyPassword; /**< Password for SOCKS5 authentication, if any */
+
   /** List of configuration lines for replacement directory authorities.
    * If you just want to replace one class of authority at a time,
    * use the "Alternate*Authority" options below instead. */
@@ -2531,6 +2564,9 @@
   /** If true, the user wants us to collect statistics as entry node. */
   int EntryStatistics;
 
+  /** If true, include statistics file contents in extra-info documents. */
+  int ExtraInfoStatistics;
+
   /** If true, do not believe anybody who tells us that a domain resolves
    * to an internal address, or that an internal address has a PTR mapping.
    * Helps avoid some cross-site attacks. */
@@ -2552,6 +2588,10 @@
   /** Location of bandwidth measurement file */
   char *V3BandwidthsFile;
 
+  /** Authority only: key=value pairs that we add to our networkstatus
+   * consensus vote on the 'params' line. */
+  char *ConsensusParams;
+
   /** The length of time that we think an initial consensus should be fresh.
    * Only altered on testing networks. */
   int TestingV3AuthInitialVotingInterval;
@@ -2644,6 +2684,10 @@
   int         BWHistoryWriteInterval;
   smartlist_t *BWHistoryWriteValues;
 
+  /** Build time histogram */
+  config_line_t * BuildtimeHistogram;
+  uint16_t TotalBuildTimes;
+
   /** What version of Tor wrote this state file? */
   char *TorVersion;
 
@@ -2738,6 +2782,7 @@
                         int force_complete);
 int fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
                          int log_sockstype, int safe_socks);
+int fetch_from_buf_socks_client(buf_t *buf, int state, char **reason);
 int fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len);
 
 int peek_buf_has_control0_command(buf_t *buf);
@@ -2812,6 +2857,155 @@
 
 void entry_guards_free_all(void);
 
+/* Circuit Build Timeout "public" functions and structures. */
+
+/** Maximum quantile to use to generate synthetic timeouts.
+ *  We want to stay a bit short of 1.0, because longtail is
+ *  loooooooooooooooooooooooooooooooooooooooooooooooooooong. */
+#define MAX_SYNTHETIC_QUANTILE 0.985
+
+/** Minimum circuits before estimating a timeout */
+#define MIN_CIRCUITS_TO_OBSERVE 500
+
+/** Total size of the circuit timeout history to accumulate.
+ * 5000 is approx 1.5 weeks worth of continual-use circuits. */
+#define NCIRCUITS_TO_OBSERVE 5000
+
+/** Width of the histogram bins in milliseconds */
+#define BUILDTIME_BIN_WIDTH ((build_time_t)50)
+
+/** Cutoff point on the CDF for our timeout estimation.
+ * TODO: This should be moved to the consensus */
+#define BUILDTIMEOUT_QUANTILE_CUTOFF 0.8
+
+/** A build_time_t is milliseconds */
+typedef uint32_t build_time_t;
+#define BUILD_TIME_MAX ((build_time_t)(INT32_MAX))
+
+/** Lowest allowable value for CircuitBuildTimeout in milliseconds */
+#define BUILD_TIMEOUT_MIN_VALUE (3*1000)
+
+/** Initial circuit build timeout in milliseconds */
+#define BUILD_TIMEOUT_INITIAL_VALUE (60*1000)
+
+/** How often in seconds should we build a test circuit */
+#define BUILD_TIMES_TEST_FREQUENCY 60
+
+/** Save state every 10 circuits */
+#define BUILD_TIMES_SAVE_STATE_EVERY 10
+
+/* Circuit Build Timeout network liveness constants */
+
+/**
+ * How many circuits count as recent when considering if the
+ * connection has gone gimpy or changed.
+ */
+#define RECENT_CIRCUITS 20
+
+/**
+ * Have we received a cell in the last N circ attempts?
+ *
+ * This tells us when to temporarily switch back to
+ * BUILD_TIMEOUT_INITIAL_VALUE until we start getting cells,
+ * at which point we switch back to computing the timeout from
+ * our saved history.
+ */
+#define NETWORK_NONLIVE_TIMEOUT_COUNT (lround(RECENT_CIRCUITS*0.15))
+
+/**
+ * This tells us when to toss out the last streak of N timeouts.
+ *
+ * If instead we start getting cells, we switch back to computing the timeout
+ * from our saved history.
+ */
+#define NETWORK_NONLIVE_DISCARD_COUNT (lround(NETWORK_NONLIVE_TIMEOUT_COUNT*2))
+
+/**
+ * Maximum count of timeouts that finish the first hop in the past
+ * RECENT_CIRCUITS before calculating a new timeout.
+ *
+ * This tells us to abandon timeout history and set
+ * the timeout back to BUILD_TIMEOUT_INITIAL_VALUE.
+ */
+#define MAX_RECENT_TIMEOUT_COUNT (lround(RECENT_CIRCUITS*0.75))
+
+/** Information about the state of our local network connection */
+typedef struct {
+  /** The timestamp we last completed a TLS handshake or received a cell */
+  time_t network_last_live;
+  /** If the network is not live, how many timeouts has this caused? */
+  int nonlive_timeouts;
+  /** If the network is not live, have we yet discarded our history? */
+  int nonlive_discarded;
+  /** Circular array of circuits that have made it to the first hop. Slot is
+   * 1 if circuit timed out, 0 if circuit succeeded */
+  int8_t timeouts_after_firsthop[RECENT_CIRCUITS];
+  /** Index into circular array. */
+  int after_firsthop_idx;
+} network_liveness_t;
+
+/** Structure for circuit build times history */
+typedef struct {
+  /** The circular array of recorded build times in milliseconds */
+  build_time_t circuit_build_times[NCIRCUITS_TO_OBSERVE];
+  /** Current index in the circuit_build_times circular array */
+  int build_times_idx;
+  /** Total number of build times accumulated. Maxes at NCIRCUITS_TO_OBSERVE */
+  int total_build_times;
+  /** Information about the state of our local network connection */
+  network_liveness_t liveness;
+  /** Last time we built a circuit. Used to decide to build new test circs */
+  time_t last_circ_at;
+  /** Number of timeouts that have happened before estimating pareto
+   *  parameters */
+  int pre_timeouts;
+  /** "Minimum" value of our pareto distribution (actually mode) */
+  build_time_t Xm;
+  /** alpha exponent for pareto dist. */
+  double alpha;
+  /** Have we computed a timeout? */
+  int have_computed_timeout;
+  /** The exact value for that timeout in milliseconds */
+  double timeout_ms;
+} circuit_build_times_t;
+
+extern circuit_build_times_t circ_times;
+void circuit_build_times_update_state(circuit_build_times_t *cbt,
+                                      or_state_t *state);
+int circuit_build_times_parse_state(circuit_build_times_t *cbt,
+                                    or_state_t *state, char **msg);
+int circuit_build_times_add_timeout(circuit_build_times_t *cbt,
+                                    int did_onehop, time_t start_time);
+void circuit_build_times_set_timeout(circuit_build_times_t *cbt);
+int circuit_build_times_add_time(circuit_build_times_t *cbt,
+                                 build_time_t time);
+int circuit_build_times_needs_circuits(circuit_build_times_t *cbt);
+int circuit_build_times_needs_circuits_now(circuit_build_times_t *cbt);
+void circuit_build_times_init(circuit_build_times_t *cbt);
+
+#ifdef CIRCUIT_PRIVATE
+double circuit_build_times_calculate_timeout(circuit_build_times_t *cbt,
+                                             double quantile);
+build_time_t circuit_build_times_generate_sample(circuit_build_times_t *cbt,
+                                                 double q_lo, double q_hi);
+void circuit_build_times_initial_alpha(circuit_build_times_t *cbt,
+                                       double quantile, double time_ms);
+void circuit_build_times_update_alpha(circuit_build_times_t *cbt);
+double circuit_build_times_cdf(circuit_build_times_t *cbt, double x);
+void circuit_build_times_add_timeout_worker(circuit_build_times_t *cbt,
+                                       double quantile_cutoff);
+void circuitbuild_running_unit_tests(void);
+void circuit_build_times_reset(circuit_build_times_t *cbt);
+
+/* Network liveness functions */
+int circuit_build_times_network_check_changed(circuit_build_times_t *cbt);
+#endif
+
+/* Network liveness functions */
+void circuit_build_times_network_is_live(circuit_build_times_t *cbt);
+int circuit_build_times_network_check_live(circuit_build_times_t *cbt);
+void circuit_build_times_network_circ_success(circuit_build_times_t *cbt);
+
 /********************************* circuitlist.c ***********************/
 
 circuit_t * _circuit_get_global_list(void);
@@ -2824,6 +3018,7 @@
                                  or_connection_t *conn);
 void circuit_set_state(circuit_t *circ, uint8_t state);
 void circuit_close_all_marked(void);
+int32_t circuit_initial_package_window(void);
 origin_circuit_t *origin_circuit_new(void);
 or_circuit_t *or_circuit_new(circid_t p_circ_id, or_connection_t *p_conn);
 circuit_t *circuit_get_by_circid_orconn(circid_t circ_id,
@@ -3000,6 +3195,10 @@
 int connection_connect(connection_t *conn, const char *address,
                        const tor_addr_t *addr,
                        uint16_t port, int *socket_error);
+
+int connection_proxy_connect(connection_t *conn, int type);
+int connection_read_proxy_handshake(connection_t *conn);
+
 int retry_all_listeners(smartlist_t *replaced_conns,
                         smartlist_t *new_conns);
 
@@ -3625,9 +3824,9 @@
                                         authority_cert_t *cert);
 
 #ifdef DIRVOTE_PRIVATE
-char *
-format_networkstatus_vote(crypto_pk_env_t *private_key,
-                          networkstatus_t *v3_ns);
+char *format_networkstatus_vote(crypto_pk_env_t *private_key,
+                                 networkstatus_t *v3_ns);
+char *dirvote_compute_params(smartlist_t *votes);
 #endif
 
 /********************************* dns.c ***************************/
@@ -3665,15 +3864,11 @@
  * leaking information. */
 #define DIR_RECORD_USAGE_GRANULARITY 8
 /** Time interval: Flush geoip data to disk this often. */
-#define DIR_RECORD_USAGE_RETAIN_IPS (24*60*60)
+#define DIR_ENTRY_RECORD_USAGE_RETAIN_IPS (24*60*60)
 /** How long do we have to have observed per-country request history before
  * we are willing to talk about it? */
-#define DIR_RECORD_USAGE_MIN_OBSERVATION_TIME (24*60*60)
+#define DIR_RECORD_USAGE_MIN_OBSERVATION_TIME (12*60*60)
 
-/** Time interval: Flush geoip data to disk this often when measuring on an
- * entry guard. */
-#define ENTRY_RECORD_USAGE_RETAIN_IPS (24*60*60)
-
 #ifdef GEOIP_PRIVATE
 int geoip_parse_entry(const char *line);
 #endif
@@ -3720,7 +3915,10 @@
 void geoip_note_ns_response(geoip_client_action_t action,
                             geoip_ns_response_t response);
 time_t geoip_get_history_start(void);
-char *geoip_get_client_history(time_t now, geoip_client_action_t action);
+char *geoip_get_client_history_dirreq(time_t now,
+                                      geoip_client_action_t action);
+char *geoip_get_client_history_bridge(time_t now,
+                                      geoip_client_action_t action);
 char *geoip_get_request_history(time_t now, geoip_client_action_t action);
 int getinfo_helper_geoip(control_connection_t *control_conn,
                          const char *question, char **answer);
@@ -3760,6 +3958,11 @@
 void geoip_change_dirreq_state(uint64_t dirreq_id, dirreq_type_t type,
                                dirreq_state_t new_state);
 
+void geoip_dirreq_stats_init(time_t now);
+void geoip_dirreq_stats_write(time_t now);
+void geoip_entry_stats_init(time_t now);
+void geoip_entry_stats_write(time_t now);
+
 /********************************* hibernate.c **********************/
 
 int accounting_parse_options(or_options_t *options, int validate_only);
@@ -3916,6 +4119,8 @@
 char *networkstatus_getinfo_helper_single(routerstatus_t *rs);
 char *networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now);
 void networkstatus_dump_bridge_status_to_file(time_t now);
+int32_t networkstatus_get_param(networkstatus_t *ns, const char *param_name,
+                                int32_t default_val);
 int getinfo_helper_networkstatus(control_connection_t *conn,
                                  const char *question, char **answer);
 void networkstatus_free_all(void);
@@ -4037,6 +4242,8 @@
 int errno_to_orconn_end_reason(int e);
 
 const char *circuit_end_reason_to_control_string(int reason);
+const char *socks4_response_code_to_string(uint8_t code);
+const char *socks5_response_code_to_string(uint8_t code);
 
 /********************************* relay.c ***************************/
 
@@ -4099,17 +4306,11 @@
 void rep_hist_dump_stats(time_t now, int severity);
 void rep_hist_note_bytes_read(size_t num_bytes, time_t when);
 void rep_hist_note_bytes_written(size_t num_bytes, time_t when);
-#ifdef ENABLE_EXIT_STATS
-void rep_hist_note_exit_bytes_read(uint16_t port, size_t num_bytes,
-                                   time_t now);
-void rep_hist_note_exit_bytes_written(uint16_t port, size_t num_bytes,
-                                      time_t now);
-void rep_hist_note_exit_stream_opened(uint16_t port, time_t now);
-#else
-#define rep_hist_note_exit_bytes_read(p,n,t) STMT_NIL
-#define rep_hist_note_exit_bytes_written(p,n,t) STMT_NIL
-#define rep_hist_note_exit_stream_opened(p,t) STMT_NIL
-#endif
+void rep_hist_note_exit_bytes_read(uint16_t port, size_t num_bytes);
+void rep_hist_note_exit_bytes_written(uint16_t port, size_t num_bytes);
+void rep_hist_note_exit_stream_opened(uint16_t port);
+void rep_hist_exit_stats_init(time_t now);
+void rep_hist_exit_stats_write(time_t now);
 int rep_hist_bandwidth_assess(void);
 char *rep_hist_get_bandwidth_lines(int for_extrainfo);
 void rep_hist_update_state(or_state_t *state);
@@ -4161,11 +4362,10 @@
 void hs_usage_write_statistics_to_file(time_t now);
 void hs_usage_free_all(void);
 
-#ifdef ENABLE_BUFFER_STATS
-#define DUMP_BUFFER_STATS_INTERVAL (24*60*60)
-void add_circ_to_buffer_stats(circuit_t *circ, time_t end_of_interval);
-void dump_buffer_stats(void);
-#endif
+void rep_hist_buffer_stats_init(time_t now);
+void rep_hist_buffer_stats_add_circ(circuit_t *circ,
+                                    time_t end_of_interval);
+void rep_hist_buffer_stats_write(time_t now);
 
 /********************************* rendclient.c ***************************/
 
@@ -4698,6 +4898,9 @@
   int patchlevel;
   char status_tag[MAX_STATUS_TAG_LEN];
   int svn_revision;
+
+  int git_tag_len;
+  char git_tag[DIGEST_LEN];
 } tor_version_t;
 
 int router_get_router_hash(const char *s, char *digest);

Modified: tor/trunk/src/or/reasons.c
===================================================================
--- tor/trunk/src/or/reasons.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/reasons.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -326,3 +326,49 @@
   }
 }
 
+/** Return a string corresponding to a SOCKS4 reponse code. */
+const char *
+socks4_response_code_to_string(uint8_t code)
+{
+  switch (code) {
+    case 0x5a:
+      return "connection accepted";
+    case 0x5b:
+      return "server rejected connection";
+    case 0x5c:
+      return "server cannot connect to identd on this client";
+    case 0x5d:
+      return "user id does not match identd";
+    default:
+      return "invalid SOCKS 4 response code";
+  }
+}
+
+/** Return a string corresponding to a SOCKS5 reponse code. */
+const char *
+socks5_response_code_to_string(uint8_t code)
+{
+  switch (code) {
+    case 0x00:
+      return "connection accepted";
+    case 0x01:
+      return "general SOCKS server failure";
+    case 0x02:
+      return "connection not allowed by ruleset";
+    case 0x03:
+      return "Network unreachable";
+    case 0x04:
+      return "Host unreachable";
+    case 0x05:
+      return "Connection refused";
+    case 0x06:
+      return "TTL expired";
+    case 0x07:
+      return "Command not supported";
+    case 0x08:
+      return "Address type not supported";
+    default:
+      return "unknown reason";
+  }
+}
+

Modified: tor/trunk/src/or/relay.c
===================================================================
--- tor/trunk/src/or/relay.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/relay.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -533,13 +533,11 @@
   log_debug(LD_OR,"delivering %d cell %s.", relay_command,
             cell_direction == CELL_DIRECTION_OUT ? "forward" : "backward");
 
-#ifdef ENABLE_DIRREQ_STATS
   /* If we are sending an END cell and this circuit is used for a tunneled
    * directory request, advance its state. */
   if (relay_command == RELAY_COMMAND_END && circ->dirreq_id)
     geoip_change_dirreq_state(circ->dirreq_id, DIRREQ_TUNNELED,
                               DIRREQ_END_CELL_SENT);
-#endif
 
   if (cell_direction == CELL_DIRECTION_OUT && circ->n_conn) {
     /* if we're using relaybandwidthrate, this conn wants priority */
@@ -1047,7 +1045,6 @@
                "Begin cell for known stream. Dropping.");
         return 0;
       }
-#ifdef ENABLE_DIRREQ_STATS
       if (rh.command == RELAY_COMMAND_BEGIN_DIR) {
         /* Assign this circuit and its app-ward OR connection a unique ID,
          * so that we can measure download times. The local edge and dir
@@ -1057,7 +1054,6 @@
         circ->dirreq_id = ++next_id;
         TO_CONN(TO_OR_CIRCUIT(circ)->p_conn)->dirreq_id = circ->dirreq_id;
       }
-#endif
 
       return connection_exit_begin_conn(cell, circ);
     case RELAY_COMMAND_DATA:
@@ -1529,6 +1525,10 @@
 /** A memory pool to allocate packed_cell_t objects. */
 static mp_pool_t *cell_pool = NULL;
 
+/** Memory pool to allocate insertion_time_elem_t objects used for cell
+ * statistics. */
+static mp_pool_t *it_pool = NULL;
+
 /** Allocate structures to hold cells. */
 void
 init_cell_pool(void)
@@ -1537,7 +1537,8 @@
   cell_pool = mp_pool_new(sizeof(packed_cell_t), 128*1024);
 }
 
-/** Free all storage used to hold cells. */
+/** Free all storage used to hold cells (and insertion times if we measure
+ * cell statistics). */
 void
 free_cell_pool(void)
 {
@@ -1546,6 +1547,10 @@
     mp_pool_destroy(cell_pool);
     cell_pool = NULL;
   }
+  if (it_pool) {
+    mp_pool_destroy(it_pool);
+    it_pool = NULL;
+  }
 }
 
 /** Free excess storage in cell pool. */
@@ -1621,11 +1626,36 @@
 cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell)
 {
   packed_cell_t *copy = packed_cell_copy(cell);
-#ifdef ENABLE_BUFFER_STATS
-  /* Remember the exact time when this cell was put in the queue. */
-  if (get_options()->CellStatistics)
-    tor_gettimeofday(&copy->packed_timeval);
-#endif
+  /* Remember the time when this cell was put in the queue. */
+  if (get_options()->CellStatistics) {
+    struct timeval now;
+    uint32_t added;
+    insertion_time_queue_t *it_queue = queue->insertion_times;
+    if (!it_pool)
+      it_pool = mp_pool_new(sizeof(insertion_time_elem_t), 1024);
+    tor_gettimeofday(&now);
+#define SECONDS_IN_A_DAY 86400L
+    added = (uint32_t)(((now.tv_sec % SECONDS_IN_A_DAY) * 100L)
+            + ((uint32_t)now.tv_usec / (uint32_t)10000L));
+    if (!it_queue) {
+      it_queue = tor_malloc_zero(sizeof(insertion_time_queue_t));
+      queue->insertion_times = it_queue;
+    }
+    if (it_queue->last && it_queue->last->insertion_time == added) {
+      it_queue->last->counter++;
+    } else {
+      insertion_time_elem_t *elem = mp_pool_get(it_pool);
+      elem->next = NULL;
+      elem->insertion_time = added;
+      elem->counter = 1;
+      if (it_queue->last) {
+        it_queue->last->next = elem;
+        it_queue->last = elem;
+      } else {
+        it_queue->first = it_queue->last = elem;
+      }
+    }
+  }
   cell_queue_append(queue, copy);
 }
 
@@ -1642,6 +1672,14 @@
   }
   queue->head = queue->tail = NULL;
   queue->n = 0;
+  if (queue->insertion_times) {
+    while (queue->insertion_times->first) {
+      insertion_time_elem_t *elem = queue->insertion_times->first;
+      queue->insertion_times->first = elem->next;
+      mp_pool_release(elem);
+    }
+    tor_free(queue->insertion_times);
+  }
 }
 
 /** Extract and return the cell at the head of <b>queue</b>; return NULL if
@@ -1835,28 +1873,43 @@
     packed_cell_t *cell = cell_queue_pop(queue);
     tor_assert(*next_circ_on_conn_p(circ,conn));
 
-#ifdef ENABLE_BUFFER_STATS
     /* Calculate the exact time that this cell has spent in the queue. */
     if (get_options()->CellStatistics && !CIRCUIT_IS_ORIGIN(circ)) {
-      struct timeval flushed_from_queue;
+      struct timeval now;
+      uint32_t flushed;
       uint32_t cell_waiting_time;
-      or_circuit_t *orcirc = TO_OR_CIRCUIT(circ);
-      tor_gettimeofday(&flushed_from_queue);
-      cell_waiting_time = (uint32_t)
-            tv_mdiff(&cell->packed_timeval, &flushed_from_queue);
+      insertion_time_queue_t *it_queue = queue->insertion_times;
+      tor_gettimeofday(&now);
+      flushed = (uint32_t)((now.tv_sec % SECONDS_IN_A_DAY) * 100L +
+                 (uint32_t)now.tv_usec / (uint32_t)10000L);
+      if (!it_queue || !it_queue->first) {
+        log_warn(LD_BUG, "Cannot determine insertion time of cell.");
+      } else {
+        or_circuit_t *orcirc = TO_OR_CIRCUIT(circ);
+        insertion_time_elem_t *elem = it_queue->first;
+        cell_waiting_time =
+            (uint32_t)((flushed * 10L + SECONDS_IN_A_DAY * 1000L -
+                        elem->insertion_time * 10L) %
+                       (SECONDS_IN_A_DAY * 1000L));
+#undef SECONDS_IN_A_DAY
+        elem->counter--;
+        if (elem->counter < 1) {
+          it_queue->first = elem->next;
+          if (elem == it_queue->last)
+            it_queue->last = NULL;
+          mp_pool_release(elem);
+        }
+        orcirc->total_cell_waiting_time += cell_waiting_time;
+        orcirc->processed_cells++;
+      }
+    }
 
-      orcirc->total_cell_waiting_time += cell_waiting_time;
-      orcirc->processed_cells++;
-    }
-#endif
-#ifdef ENABLE_DIRREQ_STATS
     /* If we just flushed our queue and this circuit is used for a
      * tunneled directory request, possibly advance its state. */
     if (queue->n == 0 && TO_CONN(conn)->dirreq_id)
       geoip_change_dirreq_state(TO_CONN(conn)->dirreq_id,
                                 DIRREQ_TUNNELED,
                                 DIRREQ_CIRC_QUEUE_FLUSHED);
-#endif
 
     connection_write_to_buf(cell->body, CELL_NETWORK_SIZE, TO_CONN(conn));
 

Modified: tor/trunk/src/or/rendclient.c
===================================================================
--- tor/trunk/src/or/rendclient.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/rendclient.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -91,8 +91,9 @@
     }
   });
   if (!intro_key) {
-    log_warn(LD_BUG, "Internal error: could not find intro key; we "
-             "only have a v2 rend desc with %d intro points.",
+    log_info(LD_REND, "Our introduction point knowledge changed in "
+             "mid-connect! Could not find intro key; we only have a "
+             "v2 rend desc with %d intro points. Giving up.",
              smartlist_len(entry->parsed->intro_nodes));
     goto err;
   }
@@ -128,7 +129,7 @@
              REND_DESC_COOKIE_LEN);
       v3_shift += 2+REND_DESC_COOKIE_LEN;
     }
-    set_uint32(tmp+v3_shift+1, htonl(time(NULL)));
+    set_uint32(tmp+v3_shift+1, htonl((uint32_t)time(NULL)));
     v3_shift += 4;
   } /* if version 2 only write version number */
   else if (entry->parsed->protocols & (1<<2)) {
@@ -644,7 +645,7 @@
   /* set the windows to default. these are the windows
    * that alice thinks bob has.
    */
-  hop->package_window = CIRCWINDOW_START;
+  hop->package_window = circuit_initial_package_window();
   hop->deliver_window = CIRCWINDOW_START;
 
   onion_append_to_cpath(&circ->cpath, hop);

Modified: tor/trunk/src/or/rendservice.c
===================================================================
--- tor/trunk/src/or/rendservice.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/rendservice.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -264,7 +264,7 @@
 
   for (line = options->RendConfigLines; line; line = line->next) {
     if (!strcasecmp(line->key, "HiddenServiceDir")) {
-      if (service) {
+      if (service) { /* register the one we just finished parsing */
         if (validate_only)
           rend_service_free(service);
         else
@@ -921,7 +921,7 @@
   len = r;
   if (*buf == 3) {
     /* Version 3 INTRODUCE2 cell. */
-    time_t ts = 0, now = time(NULL);
+    time_t ts = 0;
     v3_shift = 1;
     auth_type = buf[1];
     switch (auth_type) {
@@ -944,13 +944,12 @@
     }
 
     /* Check timestamp. */
-    memcpy((char*)&ts, buf+1+v3_shift, sizeof(uint32_t));
+    ts = ntohl(get_uint32(buf+1+v3_shift));
     v3_shift += 4;
-    ts = ntohl(ts);
     if ((now - ts) < -1 * REND_REPLAY_TIME_INTERVAL / 2 ||
         (now - ts) > REND_REPLAY_TIME_INTERVAL / 2) {
       log_warn(LD_REND, "INTRODUCE2 cell is too %s. Discarding.",
-          (now - ts) < 0 ? "old" : "new");
+               (now - ts) < 0 ? "old" : "new");
       return -1;
     }
   }
@@ -1101,7 +1100,7 @@
   circ_needs_uptime = rend_service_requires_uptime(service);
 
   /* help predict this next time */
-  rep_hist_note_used_internal(time(NULL), circ_needs_uptime, 1);
+  rep_hist_note_used_internal(now, circ_needs_uptime, 1);
 
   /* Launch a circuit to alice's chosen rendezvous point.
    */
@@ -1137,7 +1136,7 @@
   launched->build_state->pending_final_cpath = cpath =
     tor_malloc_zero(sizeof(crypt_path_t));
   cpath->magic = CRYPT_PATH_MAGIC;
-  launched->build_state->expiry_time = time(NULL) + MAX_REND_TIMEOUT;
+  launched->build_state->expiry_time = now + MAX_REND_TIMEOUT;
 
   cpath->dh_handshake_state = dh;
   dh = NULL;
@@ -1477,7 +1476,7 @@
   /* set the windows to default. these are the windows
    * that bob thinks alice has.
    */
-  hop->package_window = CIRCWINDOW_START;
+  hop->package_window = circuit_initial_package_window();
   hop->deliver_window = CIRCWINDOW_START;
 
   onion_append_to_cpath(&circuit->cpath, hop);

Modified: tor/trunk/src/or/rephist.c
===================================================================
--- tor/trunk/src/or/rephist.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/rephist.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -1320,10 +1320,7 @@
   add_obs(read_array, when, num_bytes);
 }
 
-#ifdef ENABLE_EXIT_STATS
 /* Some constants */
-/** How long are the intervals for measuring exit stats? */
-#define EXIT_STATS_INTERVAL_SEC (24 * 60 * 60)
 /** To what multiple should byte numbers be rounded up? */
 #define EXIT_STATS_ROUND_UP_BYTES 1024
 /** To what multiple should stream counts be rounded up? */
@@ -1337,118 +1334,137 @@
 /* The following data structures are arrays and no fancy smartlists or maps,
  * so that all write operations can be done in constant time. This comes at
  * the price of some memory (1.25 MB) and linear complexity when writing
- * stats. */
+ * stats for measuring relays. */
 /** Number of bytes read in current period by exit port */
-static uint64_t exit_bytes_read[EXIT_STATS_NUM_PORTS];
+static uint64_t *exit_bytes_read = NULL;
 /** Number of bytes written in current period by exit port */
-static uint64_t exit_bytes_written[EXIT_STATS_NUM_PORTS];
+static uint64_t *exit_bytes_written = NULL;
 /** Number of streams opened in current period by exit port */
-static uint32_t exit_streams[EXIT_STATS_NUM_PORTS];
+static uint32_t *exit_streams = NULL;
 
 /** When does the current exit stats period end? */
-static time_t end_of_current_exit_stats_period = 0;
+static time_t start_of_exit_stats_interval;
 
-/** Write exit stats for the current period to disk and reset counters. */
-static void
-write_exit_stats(time_t when)
+/** Initialize exit port stats. */
+void
+rep_hist_exit_stats_init(time_t now)
 {
+  start_of_exit_stats_interval = now;
+  exit_bytes_read = tor_malloc_zero(EXIT_STATS_NUM_PORTS *
+                                    sizeof(uint64_t));
+  exit_bytes_written = tor_malloc_zero(EXIT_STATS_NUM_PORTS *
+                                       sizeof(uint64_t));
+  exit_streams = tor_malloc_zero(EXIT_STATS_NUM_PORTS *
+                                 sizeof(uint32_t));
+}
+
+/** Write exit stats to $DATADIR/stats/exit-stats and reset counters. */
+void
+rep_hist_exit_stats_write(time_t now)
+{
   char t[ISO_TIME_LEN+1];
   int r, i, comma;
   uint64_t *b, total_bytes, threshold_bytes, other_bytes;
   uint32_t other_streams;
 
-  char *filename = get_datadir_fname("exit-stats");
+  char *statsdir = NULL, *filename = NULL;
   open_file_t *open_file = NULL;
   FILE *out = NULL;
 
-  log_debug(LD_HIST, "Considering writing exit port statistics to disk..");
-  while (when > end_of_current_exit_stats_period) {
-    format_iso_time(t, end_of_current_exit_stats_period);
-    log_info(LD_HIST, "Writing exit port statistics to disk for period "
-             "ending at %s.", t);
+  if (!exit_streams)
+    return; /* Not initialized */
 
-    if (!open_file) {
-      out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND,
-                                        0600, &open_file);
-      if (!out) {
-        log_warn(LD_HIST, "Couldn't open '%s'.", filename);
-        goto done;
-      }
-    }
+  statsdir = get_datadir_fname("stats");
+  if (check_private_dir(statsdir, CPD_CREATE) < 0)
+    goto done;
+  filename = get_datadir_fname("stats"PATH_SEPARATOR"exit-stats");
+  format_iso_time(t, now);
+  log_info(LD_HIST, "Writing exit port statistics to disk for period "
+           "ending at %s.", t);
 
-    /* written yyyy-mm-dd HH:MM:SS (n s) */
-    if (fprintf(out, "written %s (%d s)\n", t, EXIT_STATS_INTERVAL_SEC) < 0)
+  if (!open_file) {
+    out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND,
+                                      0600, &open_file);
+    if (!out) {
+      log_warn(LD_HIST, "Couldn't open '%s'.", filename);
       goto done;
-
-    /* Count the total number of bytes, so that we can attribute all
-     * observations below a threshold of 1 / EXIT_STATS_THRESHOLD_RECIPROCAL
-     * of all bytes to a special port 'other'. */
-    total_bytes = 0;
-    for (i = 1; i < EXIT_STATS_NUM_PORTS; i++) {
-      total_bytes += exit_bytes_read[i];
-      total_bytes += exit_bytes_written[i];
     }
-    threshold_bytes = total_bytes / EXIT_STATS_THRESHOLD_RECIPROCAL;
+  }
 
-    /* kibibytes-(read|written) port=kibibytes,.. */
-    for (r = 0; r < 2; r++) {
-      b = r ? exit_bytes_read : exit_bytes_written;
-      tor_assert(b);
-      if (fprintf(out, "%s ",
-                  r ? "kibibytes-read" : "kibibytes-written")<0)
-        goto done;
+  /* written yyyy-mm-dd HH:MM:SS (n s) */
+  if (fprintf(out, "exit-stats-end %s (%d s)\n", t,
+              (unsigned) (now - start_of_exit_stats_interval)) < 0)
+    goto done;
 
-      comma = 0;
-      other_bytes = 0;
-      for (i = 1; i < EXIT_STATS_NUM_PORTS; i++) {
-        if (b[i] > 0) {
-          if (exit_bytes_read[i] + exit_bytes_written[i] > threshold_bytes) {
-            uint64_t num = round_uint64_to_next_multiple_of(b[i],
-                                                EXIT_STATS_ROUND_UP_BYTES);
-            num /= 1024;
-            if (fprintf(out, "%s%d="U64_FORMAT,
-                        comma++ ? "," : "", i,
-                        U64_PRINTF_ARG(num)) < 0)
-              goto done;
-          } else
-            other_bytes += b[i];
-        }
-      }
-      other_bytes = round_uint64_to_next_multiple_of(other_bytes,
-                                         EXIT_STATS_ROUND_UP_BYTES);
-      other_bytes /= 1024;
-      if (fprintf(out, "%sother="U64_FORMAT"\n",
-                  comma ? "," : "", U64_PRINTF_ARG(other_bytes))<0)
-        goto done;
-    }
-    /* streams-opened port=num,.. */
-    if (fprintf(out, "streams-opened ")<0)
+  /* Count the total number of bytes, so that we can attribute all
+   * observations below a threshold of 1 / EXIT_STATS_THRESHOLD_RECIPROCAL
+   * of all bytes to a special port 'other'. */
+  total_bytes = 0;
+  for (i = 1; i < EXIT_STATS_NUM_PORTS; i++) {
+    total_bytes += exit_bytes_read[i];
+    total_bytes += exit_bytes_written[i];
+  }
+  threshold_bytes = total_bytes / EXIT_STATS_THRESHOLD_RECIPROCAL;
+
+  /* exit-kibibytes-(read|written) port=kibibytes,.. */
+  for (r = 0; r < 2; r++) {
+    b = r ? exit_bytes_read : exit_bytes_written;
+    tor_assert(b);
+    if (fprintf(out, "%s ",
+                r ? "exit-kibibytes-read"
+                  : "exit-kibibytes-written") < 0)
       goto done;
+
     comma = 0;
-    other_streams = 0;
+    other_bytes = 0;
     for (i = 1; i < EXIT_STATS_NUM_PORTS; i++) {
-      if (exit_streams[i] > 0) {
+      if (b[i] > 0) {
         if (exit_bytes_read[i] + exit_bytes_written[i] > threshold_bytes) {
-          uint32_t num = round_uint32_to_next_multiple_of(exit_streams[i],
-                                              EXIT_STATS_ROUND_UP_STREAMS);
-          if (fprintf(out, "%s%d=%u",
-                      comma++ ? "," : "", i, num)<0)
+          uint64_t num = round_uint64_to_next_multiple_of(b[i],
+                                              EXIT_STATS_ROUND_UP_BYTES);
+          num /= 1024;
+          if (fprintf(out, "%s%d="U64_FORMAT,
+                      comma++ ? "," : "", i,
+                      U64_PRINTF_ARG(num)) < 0)
             goto done;
         } else
-          other_streams += exit_streams[i];
+          other_bytes += b[i];
       }
     }
-    other_streams = round_uint32_to_next_multiple_of(other_streams,
-                                         EXIT_STATS_ROUND_UP_STREAMS);
-    if (fprintf(out, "%sother=%u\n",
-                comma ? "," : "", other_streams)<0)
+    other_bytes = round_uint64_to_next_multiple_of(other_bytes,
+                                       EXIT_STATS_ROUND_UP_BYTES);
+    other_bytes /= 1024;
+    if (fprintf(out, "%sother="U64_FORMAT"\n",
+                comma ? "," : "", U64_PRINTF_ARG(other_bytes))<0)
       goto done;
-    /* Reset counters */
-    memset(exit_bytes_read, 0, sizeof(exit_bytes_read));
-    memset(exit_bytes_written, 0, sizeof(exit_bytes_written));
-    memset(exit_streams, 0, sizeof(exit_streams));
-    end_of_current_exit_stats_period += EXIT_STATS_INTERVAL_SEC;
   }
+  /* exit-streams-opened port=num,.. */
+  if (fprintf(out, "exit-streams-opened ") < 0)
+    goto done;
+  comma = 0;
+  other_streams = 0;
+  for (i = 1; i < EXIT_STATS_NUM_PORTS; i++) {
+    if (exit_streams[i] > 0) {
+      if (exit_bytes_read[i] + exit_bytes_written[i] > threshold_bytes) {
+        uint32_t num = round_uint32_to_next_multiple_of(exit_streams[i],
+                                            EXIT_STATS_ROUND_UP_STREAMS);
+        if (fprintf(out, "%s%d=%u",
+                    comma++ ? "," : "", i, num)<0)
+          goto done;
+      } else
+        other_streams += exit_streams[i];
+    }
+  }
+  other_streams = round_uint32_to_next_multiple_of(other_streams,
+                                       EXIT_STATS_ROUND_UP_STREAMS);
+  if (fprintf(out, "%sother=%u\n",
+              comma ? "," : "", other_streams)<0)
+    goto done;
+  /* Reset counters */
+  memset(exit_bytes_read, 0, EXIT_STATS_NUM_PORTS * sizeof(uint64_t));
+  memset(exit_bytes_written, 0, EXIT_STATS_NUM_PORTS * sizeof(uint64_t));
+  memset(exit_streams, 0, EXIT_STATS_NUM_PORTS * sizeof(uint32_t));
+  start_of_exit_stats_interval = now;
 
   if (open_file)
     finish_writing_to_file(open_file);
@@ -1457,63 +1473,48 @@
   if (open_file)
     abort_writing_to_file(open_file);
   tor_free(filename);
+  tor_free(statsdir);
 }
 
-/** Prepare to add an exit stats observation at second <b>when</b> by
- * checking whether this observation lies in the current observation
- * period; if not, shift the current period forward by one until the
- * reported event fits it and write all results in between to disk. */
-static void
-add_exit_obs(time_t when)
-{
-  if (when > end_of_current_exit_stats_period) {
-    if (end_of_current_exit_stats_period)
-      write_exit_stats(when);
-    else
-      end_of_current_exit_stats_period = when + EXIT_STATS_INTERVAL_SEC;
-  }
-}
-
 /** Note that we wrote <b>num_bytes</b> to an exit connection to
- * <b>port</b> in second <b>when</b>. */
+ * <b>port</b>. */
 void
-rep_hist_note_exit_bytes_written(uint16_t port, size_t num_bytes,
-                                 time_t when)
+rep_hist_note_exit_bytes_written(uint16_t port, size_t num_bytes)
 {
   if (!get_options()->ExitPortStatistics)
     return;
-  add_exit_obs(when);
+  if (!exit_bytes_written)
+    return; /* Not initialized */
   exit_bytes_written[port] += num_bytes;
   log_debug(LD_HIST, "Written %lu bytes to exit connection to port %d.",
             (unsigned long)num_bytes, port);
 }
 
 /** Note that we read <b>num_bytes</b> from an exit connection to
- * <b>port</b> in second <b>when</b>. */
+ * <b>port</b>. */
 void
-rep_hist_note_exit_bytes_read(uint16_t port, size_t num_bytes,
-                              time_t when)
+rep_hist_note_exit_bytes_read(uint16_t port, size_t num_bytes)
 {
   if (!get_options()->ExitPortStatistics)
     return;
-  add_exit_obs(when);
+  if (!exit_bytes_read)
+    return; /* Not initialized */
   exit_bytes_read[port] += num_bytes;
   log_debug(LD_HIST, "Read %lu bytes from exit connection to port %d.",
             (unsigned long)num_bytes, port);
 }
 
-/** Note that we opened an exit stream to <b>port</b> in second
- * <b>when</b>. */
+/** Note that we opened an exit stream to <b>port</b>. */
 void
-rep_hist_note_exit_stream_opened(uint16_t port, time_t when)
+rep_hist_note_exit_stream_opened(uint16_t port)
 {
   if (!get_options()->ExitPortStatistics)
     return;
-  add_exit_obs(when);
+  if (!exit_streams)
+    return; /* Not initialized */
   exit_streams[port]++;
   log_debug(LD_HIST, "Opened exit stream to port %d", port);
 }
-#endif
 
 /** Helper: Return the largest value in b->maxima.  (This is equal to the
  * most bandwidth used in any NUM_SECS_ROLLING_MEASURE period for the last
@@ -2049,6 +2050,9 @@
   tor_free(read_array);
   tor_free(write_array);
   tor_free(last_stability_doc);
+  tor_free(exit_bytes_read);
+  tor_free(exit_bytes_written);
+  tor_free(exit_streams);
   built_last_stability_doc_at = 0;
   predicted_ports_free();
 }
@@ -2603,10 +2607,16 @@
 
 /*** cell statistics ***/
 
-#ifdef ENABLE_BUFFER_STATS
 /** Start of the current buffer stats interval. */
-time_t start_of_buffer_stats_interval;
+static time_t start_of_buffer_stats_interval;
 
+/** Initialize buffer stats. */
+void
+rep_hist_buffer_stats_init(time_t now)
+{
+  start_of_buffer_stats_interval = now;
+}
+
 typedef struct circ_buffer_stats_t {
   uint32_t processed_cells;
   double mean_num_cells_in_queue;
@@ -2621,7 +2631,7 @@
  * <b>end_of_interval</b> and reset cell counters in case the circuit
  * remains open in the next measurement interval. */
 void
-add_circ_to_buffer_stats(circuit_t *circ, time_t end_of_interval)
+rep_hist_buffer_stats_add_circ(circuit_t *circ, time_t end_of_interval)
 {
   circ_buffer_stats_t *stat;
   time_t start_of_interval;
@@ -2667,12 +2677,11 @@
     return 0;
 }
 
-/** Append buffer statistics to local file. */
+/** Write buffer statistics to $DATADIR/stats/buffer-stats. */
 void
-dump_buffer_stats(void)
+rep_hist_buffer_stats_write(time_t now)
 {
-  time_t now = time(NULL);
-  char *filename;
+  char *statsdir = NULL, *filename = NULL;
   char written[ISO_TIME_LEN+1];
   open_file_t *open_file = NULL;
   FILE *out;
@@ -2686,7 +2695,7 @@
   circuit_t *circ;
   /* add current circuits to stats */
   for (circ = _circuit_get_global_list(); circ; circ = circ->next)
-    add_circ_to_buffer_stats(circ, now);
+    rep_hist_buffer_stats_add_circ(circ, now);
   /* calculate deciles */
   memset(processed_cells, 0, SHARES * sizeof(int));
   memset(circs_in_share, 0, SHARES * sizeof(int));
@@ -2711,14 +2720,17 @@
       stat, tor_free(stat));
   smartlist_clear(circuits_for_buffer_stats);
   /* write to file */
-  filename = get_datadir_fname("buffer-stats");
+  statsdir = get_datadir_fname("stats");
+  if (check_private_dir(statsdir, CPD_CREATE) < 0)
+    goto done;
+  filename = get_datadir_fname("stats"PATH_SEPARATOR"buffer-stats");
   out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND,
                                     0600, &open_file);
   if (!out)
     goto done;
   format_iso_time(written, now);
-  if (fprintf(out, "written %s (%d s)\n", written,
-              DUMP_BUFFER_STATS_INTERVAL) < 0)
+  if (fprintf(out, "cell-stats-end %s (%d s)\n", written,
+              (unsigned) (now - start_of_buffer_stats_interval)) < 0)
     goto done;
   for (i = 0; i < SHARES; i++) {
     tor_snprintf(buf, sizeof(buf), "%d", !circs_in_share[i] ? 0 :
@@ -2726,7 +2738,7 @@
     smartlist_add(str_build, tor_strdup(buf));
   }
   str = smartlist_join_strings(str_build, ",", 0, NULL);
-  if (fprintf(out, "processed-cells %s\n", str) < 0)
+  if (fprintf(out, "cell-processed-cells %s\n", str) < 0)
     goto done;
   tor_free(str);
   SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
@@ -2737,7 +2749,7 @@
     smartlist_add(str_build, tor_strdup(buf));
   }
   str = smartlist_join_strings(str_build, ",", 0, NULL);
-  if (fprintf(out, "queued-cells %s\n", str) < 0)
+  if (fprintf(out, "cell-queued-cells %s\n", str) < 0)
     goto done;
   tor_free(str);
   SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
@@ -2748,21 +2760,23 @@
     smartlist_add(str_build, tor_strdup(buf));
   }
   str = smartlist_join_strings(str_build, ",", 0, NULL);
-  if (fprintf(out, "time-in-queue %s\n", str) < 0)
+  if (fprintf(out, "cell-time-in-queue %s\n", str) < 0)
     goto done;
   tor_free(str);
   SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
   smartlist_free(str_build);
   str_build = NULL;
-  if (fprintf(out, "number-of-circuits-per-share %d\n",
+  if (fprintf(out, "cell-circuits-per-decile %d\n",
               (number_of_circuits + SHARES - 1) / SHARES) < 0)
     goto done;
   finish_writing_to_file(open_file);
   open_file = NULL;
+  start_of_buffer_stats_interval = now;
  done:
   if (open_file)
     abort_writing_to_file(open_file);
   tor_free(filename);
+  tor_free(statsdir);
   if (str_build) {
     SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
     smartlist_free(str_build);
@@ -2770,5 +2784,4 @@
   tor_free(str);
 #undef SHARES
 }
-#endif
 

Modified: tor/trunk/src/or/router.c
===================================================================
--- tor/trunk/src/or/router.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/router.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -772,9 +772,6 @@
              me->address, me->or_port);
     circuit_launch_by_router(CIRCUIT_PURPOSE_TESTING, me,
                              CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
-    control_event_server_status(LOG_NOTICE,
-                                "CHECKING_REACHABILITY ORADDRESS=%s:%d",
-                                me->address, me->or_port);
   }
 
   tor_addr_from_ipv4h(&addr, me->addr);
@@ -790,10 +787,6 @@
                                DIR_PURPOSE_FETCH_SERVERDESC,
                                ROUTER_PURPOSE_GENERAL,
                                1, "authority.z", NULL, 0, 0);
-
-    control_event_server_status(LOG_NOTICE,
-                                "CHECKING_REACHABILITY DIRADDRESS=%s:%d",
-                                me->address, me->dir_port);
   }
 }
 
@@ -809,8 +802,11 @@
                  " Publishing server descriptor." : "");
     can_reach_or_port = 1;
     mark_my_descriptor_dirty();
-    if (!me)
+    if (!me) { /* should never happen */
+      log_warn(LD_BUG, "ORPort found reachable, but I have no routerinfo "
+               "yet. Failing to inform controller of success.");
       return;
+    }
     control_event_server_status(LOG_NOTICE,
                                 "REACHABILITY_SUCCEEDED ORADDRESS=%s:%d",
                                 me->address, me->or_port);
@@ -828,8 +824,11 @@
     can_reach_dir_port = 1;
     if (!me || decide_to_advertise_dirport(get_options(), me->dir_port))
       mark_my_descriptor_dirty();
-    if (!me)
+    if (!me) { /* should never happen */
+      log_warn(LD_BUG, "DirPort found reachable, but I have no routerinfo "
+               "yet. Failing to inform controller of success.");
       return;
+    }
     control_event_server_status(LOG_NOTICE,
                                 "REACHABILITY_SUCCEEDED DIRADDRESS=%s:%d",
                                 me->address, me->dir_port);
@@ -1269,6 +1268,7 @@
   uint32_t addr;
   char platform[256];
   int hibernating = we_are_hibernating();
+  size_t ei_size;
   or_options_t *options = get_options();
 
   if (desc_clean_since && !force)
@@ -1382,9 +1382,10 @@
   ei->cache_info.published_on = ri->cache_info.published_on;
   memcpy(ei->cache_info.identity_digest, ri->cache_info.identity_digest,
          DIGEST_LEN);
-  ei->cache_info.signed_descriptor_body = tor_malloc(8192);
-  if (extrainfo_dump_to_string(ei->cache_info.signed_descriptor_body, 8192,
-                               ei, get_identity_key()) < 0) {
+  ei_size = options->ExtraInfoStatistics ? MAX_EXTRAINFO_UPLOAD_SIZE : 8192;
+  ei->cache_info.signed_descriptor_body = tor_malloc(ei_size);
+  if (extrainfo_dump_to_string(ei->cache_info.signed_descriptor_body,
+                               ei_size, ei, get_identity_key()) < 0) {
     log_warn(LD_BUG, "Couldn't generate extra-info descriptor.");
     extrainfo_free(ei);
     return -1;
@@ -1608,8 +1609,6 @@
   return -1;
 }
 
-extern const char tor_svn_revision[]; /* from tor_main.c */
-
 /** Set <b>platform</b> (max length <b>len</b>) to a NUL-terminated short
  * string describing the version of Tor and the operating system we're
  * currently running on.
@@ -1824,6 +1823,57 @@
   return (int)written+1;
 }
 
+/** Load the contents of <b>filename</b>, find the last line starting with
+ * <b>end_line</b>, ensure that its timestamp is not more than 25 hours in
+ * the past or more than 1 hour in the future with respect to <b>now</b>,
+ * and write the file contents starting with that line to **<b>out</b>.
+ * Return 1 for success, 0 if the file does not exist or does not contain
+ * a line matching these criteria, or -1 for failure. */
+static int
+load_stats_file(const char *filename, const char *end_line, time_t now,
+                char **out)
+{
+  int r = -1;
+  char *fname = get_datadir_fname(filename);
+  char *contents, *start = NULL, *tmp, timestr[ISO_TIME_LEN+1];
+  time_t written;
+  switch (file_status(fname)) {
+    case FN_FILE:
+      /* X022 Find an alternative to reading the whole file to memory. */
+      if ((contents = read_file_to_str(fname, 0, NULL))) {
+        tmp = strstr(contents, end_line);
+        /* Find last block starting with end_line */
+        while (tmp) {
+          start = tmp;
+          tmp = strstr(tmp + 1, end_line);
+        }
+        if (!start)
+          goto notfound;
+        if (strlen(start) < strlen(end_line) + 1 + sizeof(timestr))
+          goto notfound;
+        strlcpy(timestr, start + 1 + strlen(end_line), sizeof(timestr));
+        if (parse_iso_time(timestr, &written) < 0)
+          goto notfound;
+        if (written < now - (25*60*60) || written > now + (1*60*60))
+          goto notfound;
+        *out = tor_strdup(start);
+        r = 1;
+      }
+     notfound:
+      tor_free(contents);
+      break;
+    case FN_NOENT:
+      r = 0;
+      break;
+    case FN_ERROR:
+    case FN_DIR:
+    default:
+      break;
+  }
+  tor_free(fname);
+  return r;
+}
+
 /** Write the contents of <b>extrainfo</b> to the <b>maxlen</b>-byte string
  * <b>s</b>, signing them with <b>ident_key</b>.  Return 0 on success,
  * negative on failure. */
@@ -1838,6 +1888,7 @@
   char *bandwidth_usage;
   int result;
   size_t len;
+  static int write_stats_to_extrainfo = 1;
 
   base16_encode(identity, sizeof(identity),
                 extrainfo->cache_info.identity_digest, DIGEST_LEN);
@@ -1849,6 +1900,61 @@
                         "published %s\n%s",
                         extrainfo->nickname, identity,
                         published, bandwidth_usage);
+
+  if (options->ExtraInfoStatistics && write_stats_to_extrainfo) {
+    char *contents = NULL;
+    time_t since = time(NULL) - (24*60*60);
+    log_info(LD_GENERAL, "Adding stats to extra-info descriptor.");
+    if (options->DirReqStatistics &&
+        load_stats_file("stats"PATH_SEPARATOR"dirreq-stats",
+                        "dirreq-stats-end", since, &contents) > 0) {
+      size_t pos = strlen(s);
+      if (strlcpy(s + pos, contents, maxlen - strlen(s)) !=
+          strlen(contents)) {
+        log_warn(LD_DIR, "Could not write dirreq-stats to extra-info "
+                 "descriptor.");
+        s[pos] = '\0';
+      }
+      tor_free(contents);
+    }
+    if (options->EntryStatistics &&
+        load_stats_file("stats"PATH_SEPARATOR"entry-stats",
+                        "entry-stats-end", since, &contents) > 0) {
+      size_t pos = strlen(s);
+      if (strlcpy(s + pos, contents, maxlen - strlen(s)) !=
+          strlen(contents)) {
+        log_warn(LD_DIR, "Could not write entry-stats to extra-info "
+                 "descriptor.");
+        s[pos] = '\0';
+      }
+      tor_free(contents);
+    }
+    if (options->CellStatistics &&
+        load_stats_file("stats"PATH_SEPARATOR"buffer-stats",
+                        "cell-stats-end", since, &contents) > 0) {
+      size_t pos = strlen(s);
+      if (strlcpy(s + pos, contents, maxlen - strlen(s)) !=
+          strlen(contents)) {
+        log_warn(LD_DIR, "Could not write buffer-stats to extra-info "
+                 "descriptor.");
+        s[pos] = '\0';
+      }
+      tor_free(contents);
+    }
+    if (options->ExitPortStatistics &&
+        load_stats_file("stats"PATH_SEPARATOR"exit-stats",
+                        "exit-stats-end", since, &contents) > 0) {
+      size_t pos = strlen(s);
+      if (strlcpy(s + pos, contents, maxlen - strlen(s)) !=
+          strlen(contents)) {
+        log_warn(LD_DIR, "Could not write exit-stats to extra-info "
+                 "descriptor.");
+        s[pos] = '\0';
+      }
+      tor_free(contents);
+    }
+  }
+
   tor_free(bandwidth_usage);
   if (result<0)
     return -1;
@@ -1877,7 +1983,6 @@
   if (router_append_dirobj_signature(s+len, maxlen-len, digest, ident_key)<0)
     return -1;
 
-#ifdef DEBUG_ROUTER_DUMP_ROUTER_TO_STRING
   {
     char *cp, *s_dup;
     extrainfo_t *ei_tmp;
@@ -1892,8 +1997,25 @@
     tor_free(s_dup);
     extrainfo_free(ei_tmp);
   }
-#endif
 
+  if (options->ExtraInfoStatistics && write_stats_to_extrainfo) {
+    char *cp, *s_dup;
+    extrainfo_t *ei_tmp;
+    cp = s_dup = tor_strdup(s);
+    ei_tmp = extrainfo_parse_entry_from_string(cp, NULL, 1, NULL);
+    if (!ei_tmp) {
+      log_warn(LD_GENERAL,
+               "We just generated an extra-info descriptor with "
+               "statistics that we can't parse. Not adding statistics to "
+               "this or any future extra-info descriptors. Descriptor "
+               "was:\n%s", s);
+      write_stats_to_extrainfo = 0;
+      extrainfo_dump_to_string(s, maxlen, extrainfo, ident_key);
+    }
+    tor_free(s_dup);
+    extrainfo_free(ei_tmp);
+  }
+
   return (int)strlen(s)+1;
 }
 
@@ -1907,13 +2029,9 @@
 extrainfo_get_client_geoip_summary(time_t now)
 {
   static time_t last_purged_at = 0;
-  int geoip_purge_interval = 48*60*60;
-#ifdef ENABLE_DIRREQ_STATS
-  geoip_purge_interval = DIR_RECORD_USAGE_RETAIN_IPS;
-#endif
-#ifdef ENABLE_ENTRY_STATS
-  geoip_purge_interval = ENTRY_RECORD_USAGE_RETAIN_IPS;
-#endif
+  int geoip_purge_interval =
+      (get_options()->DirReqStatistics || get_options()->EntryStatistics) ?
+      DIR_ENTRY_RECORD_USAGE_RETAIN_IPS : 48*60*60;
   if (now > last_purged_at+geoip_purge_interval) {
     /* (Note that this also discards items in the client history with
      * action GEOIP_CLIENT_NETWORKSTATUS{_V2}, which doesn't matter
@@ -1922,7 +2040,7 @@
     geoip_remove_old_clients(now-geoip_purge_interval);
     last_purged_at = now;
   }
-  return geoip_get_client_history(now, GEOIP_CLIENT_CONNECT);
+  return geoip_get_client_history_bridge(now, GEOIP_CLIENT_CONNECT);
 }
 
 /** Return true iff <b>s</b> is a legally valid server nickname. */

Modified: tor/trunk/src/or/routerlist.c
===================================================================
--- tor/trunk/src/or/routerlist.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/routerlist.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -156,21 +156,24 @@
 
 /** Load a bunch of new key certificates from the string <b>contents</b>.  If
  * <b>from_store</b> is true, the certificates are from the cache, and we
- * don't need to flush them to disk.  If <b>from_store</b> is false, we need
- * to flush any changed certificates to disk.  Return 0 on success, -1 on
- * failure. */
+ * don't need to flush them to disk. If <b>flush</b> is true, we need
+ * to flush any changed certificates to disk now.  Return 0 on success, -1
+ * if any certs fail to parse. */
 int
 trusted_dirs_load_certs_from_string(const char *contents, int from_store,
                                     int flush)
 {
   trusted_dir_server_t *ds;
   const char *s, *eos;
+  int failure_code = 0;
 
   for (s = contents; *s; s = eos) {
     authority_cert_t *cert = authority_cert_parse_from_string(s, &eos);
     cert_list_t *cl;
-    if (!cert)
+    if (!cert) {
+      failure_code = -1;
       break;
+    }
     ds = trusteddirserver_get_by_v3_auth_digest(
                                        cert->cache_info.identity_digest);
     log_debug(LD_DIR, "Parsed certificate for %s",
@@ -224,7 +227,7 @@
            ds->dir_port != cert->dir_port)) {
         char *a = tor_dup_ip(cert->addr);
         log_notice(LD_DIR, "Updating address for directory authority %s "
-                   "from %s:%d to %s:%d based on in certificate.",
+                   "from %s:%d to %s:%d based on certificate.",
                    ds->nickname, ds->address, (int)ds->dir_port,
                    a, cert->dir_port);
         tor_free(a);
@@ -241,8 +244,11 @@
   if (flush)
     trusted_dirs_flush_certs_to_disk();
 
+  /* call this even if failure_code is <0, since some certs might have
+   * succeeded. */
   networkstatus_note_certs_arrived();
-  return 0;
+
+  return failure_code;
 }
 
 /** Save all v3 key certificates to the cached-certs file. */

Modified: tor/trunk/src/or/routerparse.c
===================================================================
--- tor/trunk/src/or/routerparse.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/routerparse.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -62,6 +62,31 @@
   K_HIDDEN_SERVICE_DIR,
   K_ALLOW_SINGLE_HOP_EXITS,
 
+  K_DIRREQ_END,
+  K_DIRREQ_V2_IPS,
+  K_DIRREQ_V3_IPS,
+  K_DIRREQ_V2_REQS,
+  K_DIRREQ_V3_REQS,
+  K_DIRREQ_V2_SHARE,
+  K_DIRREQ_V3_SHARE,
+  K_DIRREQ_V2_RESP,
+  K_DIRREQ_V3_RESP,
+  K_DIRREQ_V2_DIR,
+  K_DIRREQ_V3_DIR,
+  K_DIRREQ_V2_TUN,
+  K_DIRREQ_V3_TUN,
+  K_ENTRY_END,
+  K_ENTRY_IPS,
+  K_CELL_END,
+  K_CELL_PROCESSED,
+  K_CELL_QUEUED,
+  K_CELL_TIME,
+  K_CELL_CIRCS,
+  K_EXIT_END,
+  K_EXIT_WRITTEN,
+  K_EXIT_READ,
+  K_EXIT_OPENED,
+
   K_DIR_KEY_CERTIFICATE_VERSION,
   K_DIR_IDENTITY_KEY,
   K_DIR_KEY_PUBLISHED,
@@ -77,6 +102,7 @@
   K_VOTING_DELAY,
 
   K_KNOWN_FLAGS,
+  K_PARAMS,
   K_VOTE_DIGEST,
   K_CONSENSUS_DIGEST,
   K_CONSENSUS_METHODS,
@@ -257,6 +283,31 @@
   T0N("opt",                 K_OPT,             CONCAT_ARGS, OBJ_OK ),
   T01("read-history",        K_READ_HISTORY,        ARGS,    NO_OBJ ),
   T01("write-history",       K_WRITE_HISTORY,       ARGS,    NO_OBJ ),
+  T01("dirreq-stats-end",    K_DIRREQ_END,          ARGS,    NO_OBJ ),
+  T01("dirreq-v2-ips",       K_DIRREQ_V2_IPS,       ARGS,    NO_OBJ ),
+  T01("dirreq-v3-ips",       K_DIRREQ_V3_IPS,       ARGS,    NO_OBJ ),
+  T01("dirreq-v2-reqs",      K_DIRREQ_V2_REQS,      ARGS,    NO_OBJ ),
+  T01("dirreq-v3-reqs",      K_DIRREQ_V3_REQS,      ARGS,    NO_OBJ ),
+  T01("dirreq-v2-share",     K_DIRREQ_V2_SHARE,     ARGS,    NO_OBJ ),
+  T01("dirreq-v3-share",     K_DIRREQ_V3_SHARE,     ARGS,    NO_OBJ ),
+  T01("dirreq-v2-resp",      K_DIRREQ_V2_RESP,      ARGS,    NO_OBJ ),
+  T01("dirreq-v3-resp",      K_DIRREQ_V3_RESP,      ARGS,    NO_OBJ ),
+  T01("dirreq-v2-direct-dl", K_DIRREQ_V2_DIR,       ARGS,    NO_OBJ ),
+  T01("dirreq-v3-direct-dl", K_DIRREQ_V3_DIR,       ARGS,    NO_OBJ ),
+  T01("dirreq-v2-tunneled-dl", K_DIRREQ_V2_TUN,     ARGS,    NO_OBJ ),
+  T01("dirreq-v3-tunneled-dl", K_DIRREQ_V3_TUN,     ARGS,    NO_OBJ ),
+  T01("entry-stats-end",     K_ENTRY_END,           ARGS,    NO_OBJ ),
+  T01("entry-ips",           K_ENTRY_IPS,           ARGS,    NO_OBJ ),
+  T01("cell-stats-end",      K_CELL_END,            ARGS,    NO_OBJ ),
+  T01("cell-processed-cells", K_CELL_PROCESSED,     ARGS,    NO_OBJ ),
+  T01("cell-queued-cells",   K_CELL_QUEUED,         ARGS,    NO_OBJ ),
+  T01("cell-time-in-queue",  K_CELL_TIME,           ARGS,    NO_OBJ ),
+  T01("cell-circuits-per-decile", K_CELL_CIRCS,     ARGS,    NO_OBJ ),
+  T01("exit-stats-end",      K_EXIT_END,            ARGS,    NO_OBJ ),
+  T01("exit-kibibytes-written", K_EXIT_WRITTEN,     ARGS,    NO_OBJ ),
+  T01("exit-kibibytes-read", K_EXIT_READ,           ARGS,    NO_OBJ ),
+  T01("exit-streams-opened", K_EXIT_OPENED,         ARGS,    NO_OBJ ),
+
   T1_START( "extra-info",          K_EXTRA_INFO,          GE(2),   NO_OBJ ),
 
   END_OF_TABLE
@@ -383,6 +434,7 @@
   T1("valid-until",            K_VALID_UNTIL,      CONCAT_ARGS, NO_OBJ ),
   T1("voting-delay",           K_VOTING_DELAY,     GE(2),       NO_OBJ ),
   T1("known-flags",            K_KNOWN_FLAGS,      ARGS,        NO_OBJ ),
+  T01("params",                K_PARAMS,           ARGS,        NO_OBJ ),
   T( "fingerprint",            K_FINGERPRINT,      CONCAT_ARGS, NO_OBJ ),
 
   CERTIFICATE_MEMBERS
@@ -420,6 +472,7 @@
   T01("client-versions",     K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ),
   T01("server-versions",     K_SERVER_VERSIONS, CONCAT_ARGS, NO_OBJ ),
   T01("consensus-method",    K_CONSENSUS_METHOD,    EQ(1),   NO_OBJ),
+  T01("params",                K_PARAMS,           ARGS,        NO_OBJ ),
 
   END_OF_TABLE
 };
@@ -495,6 +548,34 @@
 #define DUMP_AREA(a,name) STMT_NIL
 #endif
 
+/** Last time we dumped a descriptor to disk. */
+static time_t last_desc_dumped = 0;
+
+/** For debugging purposes, dump unparseable descriptor *<b>desc</b> of
+ * type *<b>type</b> to file $DATADIR/unparseable-desc. Do not write more
+ * than one descriptor to disk per minute. If there is already such a
+ * file in the data directory, overwrite it. */
+static void
+dump_desc(const char *desc, const char *type)
+{
+  time_t now = time(NULL);
+  tor_assert(desc);
+  tor_assert(type);
+  if (!last_desc_dumped || last_desc_dumped + 60 < now) {
+    char *debugfile = get_datadir_fname("unparseable-desc");
+    size_t filelen = 50 + strlen(type) + strlen(desc);
+    char *content = tor_malloc_zero(filelen);
+    tor_snprintf(content, filelen, "Unable to parse descriptor of type "
+                 "%s:\n%s", type, desc);
+    write_str_to_file(debugfile, content, 0);
+    log_info(LD_DIR, "Unable to parse descriptor of type %s. See file "
+             "unparseable-desc in data directory for details.", type);
+    tor_free(content);
+    tor_free(debugfile);
+    last_desc_dumped = now;
+  }
+}
+
 /** Set <b>digest</b> to the SHA-1 digest of the hash of the directory in
  * <b>s</b>.  Return 0 on success, -1 on failure.
  */
@@ -684,7 +765,7 @@
   char digest[DIGEST_LEN];
   time_t published_on;
   int r;
-  const char *end, *cp;
+  const char *end, *cp, *str_dup = str;
   smartlist_t *tokens = NULL;
   crypto_pk_env_t *declared_key = NULL;
   memarea_t *area = memarea_new();
@@ -757,6 +838,7 @@
   r = 0;
   goto done;
  err:
+  dump_desc(str_dup, "v1 directory");
   r = -1;
  done:
   if (declared_key) crypto_free_pk_env(declared_key);
@@ -783,7 +865,7 @@
   int r = -1;
   crypto_pk_env_t *declared_key = NULL;
   smartlist_t *tokens = NULL;
-  const char *eos = str + strlen(str);
+  const char *eos = str + strlen(str), *str_dup = str;
   memarea_t *area = NULL;
 
   if (router_get_runningrouters_hash(str, digest)) {
@@ -824,6 +906,7 @@
 
   r = 0;
  err:
+  dump_desc(str_dup, "v1 running-routers");
   if (declared_key) crypto_free_pk_env(declared_key);
   if (tokens) {
     SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
@@ -1133,7 +1216,7 @@
   smartlist_t *tokens = NULL, *exit_policy_tokens = NULL;
   directory_token_t *tok;
   struct in_addr in;
-  const char *start_of_annotations, *cp;
+  const char *start_of_annotations, *cp, *s_dup = s;
   size_t prepend_len = prepend_annotations ? strlen(prepend_annotations) : 0;
   int ok = 1;
   memarea_t *area = NULL;
@@ -1423,6 +1506,7 @@
   goto done;
 
  err:
+  dump_desc(s_dup, "router descriptor");
   routerinfo_free(router);
   router = NULL;
  done:
@@ -1457,6 +1541,7 @@
   crypto_pk_env_t *key = NULL;
   routerinfo_t *router = NULL;
   memarea_t *area = NULL;
+  const char *s_dup = s;
 
   if (!end) {
     end = s + strlen(s);
@@ -1545,6 +1630,7 @@
 
   goto done;
  err:
+  dump_desc(s_dup, "extra-info descriptor");
   if (extrainfo)
     extrainfo_free(extrainfo);
   extrainfo = NULL;
@@ -1574,6 +1660,7 @@
   size_t len;
   int found;
   memarea_t *area = NULL;
+  const char *s_dup = s;
 
   s = eat_whitespace(s);
   eos = strstr(s, "\ndir-key-certification");
@@ -1727,6 +1814,7 @@
   }
   return cert;
  err:
+  dump_desc(s_dup, "authority cert");
   authority_cert_free(cert);
   SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
   smartlist_free(tokens);
@@ -1777,7 +1865,7 @@
                                      vote_routerstatus_t *vote_rs,
                                      int consensus_method)
 {
-  const char *eos;
+  const char *eos, *s_dup = *s;
   routerstatus_t *rs = NULL;
   directory_token_t *tok;
   char timebuf[ISO_TIME_LEN+1];
@@ -1917,8 +2005,9 @@
     for (i=0; i < tok->n_args; ++i) {
       if (!strcmpstart(tok->args[i], "Bandwidth=")) {
         int ok;
-        rs->bandwidth = tor_parse_ulong(strchr(tok->args[i], '=')+1, 10,
-                                        0, UINT32_MAX, &ok, NULL);
+        rs->bandwidth = (uint32_t)tor_parse_ulong(strchr(tok->args[i], '=')+1,
+                                                  10, 0, UINT32_MAX,
+                                                  &ok, NULL);
         if (!ok) {
           log_warn(LD_DIR, "Invalid Bandwidth %s", escaped(tok->args[i]));
           goto err;
@@ -1926,8 +2015,9 @@
         rs->has_bandwidth = 1;
       } else if (!strcmpstart(tok->args[i], "Measured=")) {
         int ok;
-        rs->measured_bw = tor_parse_ulong(strchr(tok->args[i], '=')+1, 10,
-                                          0, UINT32_MAX, &ok, NULL);
+        rs->measured_bw =
+            (uint32_t)tor_parse_ulong(strchr(tok->args[i], '=')+1,
+                                      10, 0, UINT32_MAX, &ok, NULL);
         if (!ok) {
           log_warn(LD_DIR, "Invalid Measured Bandwidth %s",
                    escaped(tok->args[i]));
@@ -1960,6 +2050,7 @@
 
   goto done;
  err:
+  dump_desc(s_dup, "routerstatus entry");
   if (rs && !vote_rs)
     routerstatus_free(rs);
   rs = NULL;
@@ -2001,7 +2092,7 @@
 networkstatus_v2_t *
 networkstatus_v2_parse_from_string(const char *s)
 {
-  const char *eos;
+  const char *eos, *s_dup = s;
   smartlist_t *tokens = smartlist_create();
   smartlist_t *footer_tokens = smartlist_create();
   networkstatus_v2_t *ns = NULL;
@@ -2150,6 +2241,7 @@
 
   goto done;
  err:
+  dump_desc(s_dup, "v2 networkstatus");
   if (ns)
     networkstatus_v2_free(ns);
   ns = NULL;
@@ -2176,7 +2268,7 @@
   networkstatus_voter_info_t *voter = NULL;
   networkstatus_t *ns = NULL;
   char ns_digest[DIGEST_LEN];
-  const char *cert, *end_of_header, *end_of_footer;
+  const char *cert, *end_of_header, *end_of_footer, *s_dup = s;
   directory_token_t *tok;
   int ok;
   struct in_addr in;
@@ -2319,6 +2411,34 @@
     goto err;
   }
 
+  tok = find_opt_by_keyword(tokens, K_PARAMS);
+  if (tok) {
+    inorder = 1;
+    ns->net_params = smartlist_create();
+    for (i = 0; i < tok->n_args; ++i) {
+      int ok=0;
+      char *eq = strchr(tok->args[i], '=');
+      if (!eq) {
+        log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
+        goto err;
+      }
+      tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
+      if (!ok) {
+        log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
+        goto err;
+      }
+      if (i > 0 && strcmp(tok->args[i-1], tok->args[i]) >= 0) {
+        log_warn(LD_DIR, "%s >= %s", tok->args[i-1], tok->args[i]);
+        inorder = 0;
+      }
+      smartlist_add(ns->net_params, tor_strdup(tok->args[i]));
+    }
+    if (!inorder) {
+      log_warn(LD_DIR, "params not in order");
+      goto err;
+    }
+  }
+
   ns->voters = smartlist_create();
 
   SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
@@ -2518,6 +2638,14 @@
     } else {
       if (tok->object_size >= INT_MAX)
         goto err;
+      /* We already parsed a vote from this voter. Use the first one. */
+      if (v->signature) {
+        log_fn(LOG_PROTOCOL_WARN, LD_DIR, "We received a networkstatus "
+                   "that contains two votes from the same voter. Ignoring "
+                   "the second vote.");
+        continue;
+      }
+
       v->signature = tor_memdup(tok->object_body, tok->object_size);
       v->signature_len = (int) tok->object_size;
     }
@@ -2527,6 +2655,10 @@
   if (! n_signatures) {
     log_warn(LD_DIR, "No signatures on networkstatus vote.");
     goto err;
+  } else if (ns->type == NS_TYPE_VOTE && n_signatures != 1) {
+    log_warn(LD_DIR, "Received more than one signature on a "
+             "network-status vote.");
+    goto err;
   }
 
   if (eos_out)
@@ -2534,6 +2666,7 @@
 
   goto done;
  err:
+  dump_desc(s_dup, "v3 networkstatus");
   if (ns)
     networkstatus_vote_free(ns);
   ns = NULL;
@@ -3325,7 +3458,7 @@
   if (!*start) return 0;
   s = (char *)find_whitespace(start); /* also finds '\0', which is fine */
   s2 = (char*)eat_whitespace(s);
-  if (!strcmpstart(s2, "(r"))
+  if (!strcmpstart(s2, "(r") || !strcmpstart(s2, "(git-"))
     s = (char*)find_whitespace(s2);
 
   if ((size_t)(s-start+1) >= sizeof(tmp)) /* too big, no */
@@ -3421,6 +3554,23 @@
   if (!strcmpstart(cp, "(r")) {
     cp += 2;
     out->svn_revision = (int) strtol(cp,&eos,10);
+  } else if (!strcmpstart(cp, "(git-")) {
+    char *close_paren = strchr(cp, ')');
+    int hexlen;
+    char digest[DIGEST_LEN];
+    if (! close_paren)
+      return -1;
+    cp += 5;
+    if (close_paren-cp > HEX_DIGEST_LEN)
+      return -1;
+    hexlen = (int)(close_paren-cp);
+    memset(digest, 0, sizeof(digest));
+    if ( hexlen == 0 || (hexlen % 2) == 1)
+      return -1;
+    if (base16_decode(digest, hexlen/2, cp, hexlen))
+      return -1;
+    memcpy(out->git_tag, digest, hexlen/2);
+    out->git_tag_len = hexlen/2;
   }
 
   return 0;
@@ -3446,8 +3596,14 @@
     return i;
   else if ((i = strcmp(a->status_tag, b->status_tag)))
     return i;
+  else if ((i = a->svn_revision - b->svn_revision))
+    return i;
+  else if ((i = a->git_tag_len - b->git_tag_len))
+    return i;
+  else if (a->git_tag_len)
+    return memcmp(a->git_tag, b->git_tag, a->git_tag_len);
   else
-    return a->svn_revision - b->svn_revision;
+    return 0;
 }
 
 /** Return true iff versions <b>a</b> and <b>b</b> belong to the same series.

Modified: tor/trunk/src/or/tor_main.c
===================================================================
--- tor/trunk/src/or/tor_main.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/or/tor_main.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -7,7 +7,7 @@
  * built from.  This string is generated by a bit of shell kludging int
  * src/or/Makefile.am, and is usually right.
  */
-const char tor_svn_revision[] =
+const char tor_git_revision[] =
 #ifndef _MSC_VER
 #include "micro-revision.i"
 #endif


Property changes on: tor/trunk/src/test
___________________________________________________________________
Added: svn:ignore
   + Makefile.in
Makefile
test
.deps
*.gcov
*.gcno
*.gcda
*.orig
*.rej


Added: tor/trunk/src/test/Makefile.am
===================================================================
--- tor/trunk/src/test/Makefile.am	                        (rev 0)
+++ tor/trunk/src/test/Makefile.am	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,31 @@
+TESTS = test
+
+noinst_PROGRAMS = test
+
+AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
+        -DLOCALSTATEDIR="\"$(localstatedir)\"" \
+        -DBINDIR="\"$(bindir)\""
+
+AM_CFLAGS = -I../or
+
+# -L flags need to go in LDFLAGS. -l flags need to go in LDADD.
+# This seems to matter nowhere but on windows, but I assure you that it
+# matters a lot there, and is quite hard to debug if you forget to do it.
+
+test_SOURCES = \
+	test_data.c \
+	test.c \
+	test_addr.c \
+	test_crypto.c \
+	test_dir.c \
+	test_containers.c \
+	test_util.c \
+	tinytest.c
+
+test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
+        @TOR_LDFLAGS_libevent@
+test_LDADD = ../or/libtor.a ../common/libor.a ../common/libor-crypto.a \
+	../common/libor-event.a \
+	-lz -lm -levent -lssl -lcrypto @TOR_LIB_WS32@ @TOR_LIB_GDI@
+
+noinst_HEADERS = tinytest.h tinytest_macros.h

Added: tor/trunk/src/test/test.c
===================================================================
--- tor/trunk/src/test/test.c	                        (rev 0)
+++ tor/trunk/src/test/test.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,1212 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2009, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/* Ordinarily defined in tor_main.c; this bit is just here to provide one
+ * since we're not linking to tor_main.c */
+const char tor_git_revision[] = "";
+
+/**
+ * \file test.c
+ * \brief Unit tests for many pieces of the lower level Tor modules.
+ **/
+
+#include "orconfig.h"
+
+#include <stdio.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef MS_WINDOWS
+/* For mkdir() */
+#include <direct.h>
+#else
+#include <dirent.h>
+#endif
+
+/* These macros pull in declarations for some functions and structures that
+ * are typically file-private. */
+#define BUFFERS_PRIVATE
+#define CONFIG_PRIVATE
+#define GEOIP_PRIVATE
+#define ROUTER_PRIVATE
+#define CIRCUIT_PRIVATE
+
+/*
+ * Linux doesn't provide lround in math.h by default, but mac os does...
+ * It's best just to leave math.h out of the picture entirely.
+ */
+//#include <math.h>
+long int lround(double x);
+double fabs(double x);
+
+#include "or.h"
+#include "test.h"
+#include "torgzip.h"
+#include "mempool.h"
+#include "memarea.h"
+
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#include <openssl/crypto.h>
+#endif
+
+/** Set to true if any unit test has failed.  Mostly, this is set by the macros
+ * in test.h */
+int have_failed = 0;
+
+/** Temporary directory (set up by setup_directory) under which we store all
+ * our files during testing. */
+static char temp_dir[256];
+
+/** Select and create the temporary directory we'll use to run our unit tests.
+ * Store it in <b>temp_dir</b>.  Exit immediately if we can't create it.
+ * idempotent. */
+static void
+setup_directory(void)
+{
+  static int is_setup = 0;
+  int r;
+  if (is_setup) return;
+
+#ifdef MS_WINDOWS
+  // XXXX
+  tor_snprintf(temp_dir, sizeof(temp_dir),
+               "c:\\windows\\temp\\tor_test_%d", (int)getpid());
+  r = mkdir(temp_dir);
+#else
+  tor_snprintf(temp_dir, sizeof(temp_dir), "/tmp/tor_test_%d", (int) getpid());
+  r = mkdir(temp_dir, 0700);
+#endif
+  if (r) {
+    fprintf(stderr, "Can't create directory %s:", temp_dir);
+    perror("");
+    exit(1);
+  }
+  is_setup = 1;
+}
+
+/** Return a filename relative to our testing temporary directory */
+const char *
+get_fname(const char *name)
+{
+  static char buf[1024];
+  setup_directory();
+  tor_snprintf(buf,sizeof(buf),"%s/%s",temp_dir,name);
+  return buf;
+}
+
+/** Remove all files stored under the temporary directory, and the directory
+ * itself. */
+static void
+remove_directory(void)
+{
+  smartlist_t *elements = tor_listdir(temp_dir);
+  if (elements) {
+    SMARTLIST_FOREACH(elements, const char *, cp,
+       {
+         size_t len = strlen(cp)+strlen(temp_dir)+16;
+         char *tmp = tor_malloc(len);
+         tor_snprintf(tmp, len, "%s"PATH_SEPARATOR"%s", temp_dir, cp);
+         unlink(tmp);
+         tor_free(tmp);
+       });
+    SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
+    smartlist_free(elements);
+  }
+  rmdir(temp_dir);
+}
+
+/** Define this if unit tests spend too much time generating public keys*/
+#undef CACHE_GENERATED_KEYS
+
+static crypto_pk_env_t *pregen_keys[5] = {NULL, NULL, NULL, NULL, NULL};
+#define N_PREGEN_KEYS ((int)(sizeof(pregen_keys)/sizeof(pregen_keys[0])))
+
+/** Generate and return a new keypair for use in unit tests.  If we're using
+ * the key cache optimization, we might reuse keys: we only guarantee that
+ * keys made with distinct values for <b>idx</b> are different.  The value of
+ * <b>idx</b> must be at least 0, and less than N_PREGEN_KEYS. */
+crypto_pk_env_t *
+pk_generate(int idx)
+{
+#ifdef CACHE_GENERATED_KEYS
+  tor_assert(idx < N_PREGEN_KEYS);
+  if (! pregen_keys[idx]) {
+    pregen_keys[idx] = crypto_new_pk_env();
+    tor_assert(!crypto_pk_generate_key(pregen_keys[idx]));
+  }
+  return crypto_pk_dup_key(pregen_keys[idx]);
+#else
+  crypto_pk_env_t *result;
+  (void) idx;
+  result = crypto_new_pk_env();
+  tor_assert(!crypto_pk_generate_key(result));
+  return result;
+#endif
+}
+
+/** Free all storage used for the cached key optimization. */
+static void
+free_pregenerated_keys(void)
+{
+  unsigned idx;
+  for (idx = 0; idx < N_PREGEN_KEYS; ++idx) {
+    if (pregen_keys[idx]) {
+      crypto_free_pk_env(pregen_keys[idx]);
+      pregen_keys[idx] = NULL;
+    }
+  }
+}
+
+/** Run unit tests for buffers.c */
+static void
+test_buffers(void)
+{
+  char str[256];
+  char str2[256];
+
+  buf_t *buf = NULL, *buf2 = NULL;
+  const char *cp;
+
+  int j;
+  size_t r;
+
+  /****
+   * buf_new
+   ****/
+  if (!(buf = buf_new()))
+    test_fail();
+
+  //test_eq(buf_capacity(buf), 4096);
+  test_eq(buf_datalen(buf), 0);
+
+  /****
+   * General pointer frobbing
+   */
+  for (j=0;j<256;++j) {
+    str[j] = (char)j;
+  }
+  write_to_buf(str, 256, buf);
+  write_to_buf(str, 256, buf);
+  test_eq(buf_datalen(buf), 512);
+  fetch_from_buf(str2, 200, buf);
+  test_memeq(str, str2, 200);
+  test_eq(buf_datalen(buf), 312);
+  memset(str2, 0, sizeof(str2));
+
+  fetch_from_buf(str2, 256, buf);
+  test_memeq(str+200, str2, 56);
+  test_memeq(str, str2+56, 200);
+  test_eq(buf_datalen(buf), 56);
+  memset(str2, 0, sizeof(str2));
+  /* Okay, now we should be 512 bytes into the 4096-byte buffer.  If we add
+   * another 3584 bytes, we hit the end. */
+  for (j=0;j<15;++j) {
+    write_to_buf(str, 256, buf);
+  }
+  assert_buf_ok(buf);
+  test_eq(buf_datalen(buf), 3896);
+  fetch_from_buf(str2, 56, buf);
+  test_eq(buf_datalen(buf), 3840);
+  test_memeq(str+200, str2, 56);
+  for (j=0;j<15;++j) {
+    memset(str2, 0, sizeof(str2));
+    fetch_from_buf(str2, 256, buf);
+    test_memeq(str, str2, 256);
+  }
+  test_eq(buf_datalen(buf), 0);
+  buf_free(buf);
+  buf = NULL;
+
+  /* Okay, now make sure growing can work. */
+  buf = buf_new_with_capacity(16);
+  //test_eq(buf_capacity(buf), 16);
+  write_to_buf(str+1, 255, buf);
+  //test_eq(buf_capacity(buf), 256);
+  fetch_from_buf(str2, 254, buf);
+  test_memeq(str+1, str2, 254);
+  //test_eq(buf_capacity(buf), 256);
+  assert_buf_ok(buf);
+  write_to_buf(str, 32, buf);
+  //test_eq(buf_capacity(buf), 256);
+  assert_buf_ok(buf);
+  write_to_buf(str, 256, buf);
+  assert_buf_ok(buf);
+  //test_eq(buf_capacity(buf), 512);
+  test_eq(buf_datalen(buf), 33+256);
+  fetch_from_buf(str2, 33, buf);
+  test_eq(*str2, str[255]);
+
+  test_memeq(str2+1, str, 32);
+  //test_eq(buf_capacity(buf), 512);
+  test_eq(buf_datalen(buf), 256);
+  fetch_from_buf(str2, 256, buf);
+  test_memeq(str, str2, 256);
+
+  /* now try shrinking: case 1. */
+  buf_free(buf);
+  buf = buf_new_with_capacity(33668);
+  for (j=0;j<67;++j) {
+    write_to_buf(str,255, buf);
+  }
+  //test_eq(buf_capacity(buf), 33668);
+  test_eq(buf_datalen(buf), 17085);
+  for (j=0; j < 40; ++j) {
+    fetch_from_buf(str2, 255,buf);
+    test_memeq(str2, str, 255);
+  }
+
+  /* now try shrinking: case 2. */
+  buf_free(buf);
+  buf = buf_new_with_capacity(33668);
+  for (j=0;j<67;++j) {
+    write_to_buf(str,255, buf);
+  }
+  for (j=0; j < 20; ++j) {
+    fetch_from_buf(str2, 255,buf);
+    test_memeq(str2, str, 255);
+  }
+  for (j=0;j<80;++j) {
+    write_to_buf(str,255, buf);
+  }
+  //test_eq(buf_capacity(buf),33668);
+  for (j=0; j < 120; ++j) {
+    fetch_from_buf(str2, 255,buf);
+    test_memeq(str2, str, 255);
+  }
+
+  /* Move from buf to buf. */
+  buf_free(buf);
+  buf = buf_new_with_capacity(4096);
+  buf2 = buf_new_with_capacity(4096);
+  for (j=0;j<100;++j)
+    write_to_buf(str, 255, buf);
+  test_eq(buf_datalen(buf), 25500);
+  for (j=0;j<100;++j) {
+    r = 10;
+    move_buf_to_buf(buf2, buf, &r);
+    test_eq(r, 0);
+  }
+  test_eq(buf_datalen(buf), 24500);
+  test_eq(buf_datalen(buf2), 1000);
+  for (j=0;j<3;++j) {
+    fetch_from_buf(str2, 255, buf2);
+    test_memeq(str2, str, 255);
+  }
+  r = 8192; /*big move*/
+  move_buf_to_buf(buf2, buf, &r);
+  test_eq(r, 0);
+  r = 30000; /* incomplete move */
+  move_buf_to_buf(buf2, buf, &r);
+  test_eq(r, 13692);
+  for (j=0;j<97;++j) {
+    fetch_from_buf(str2, 255, buf2);
+    test_memeq(str2, str, 255);
+  }
+  buf_free(buf);
+  buf_free(buf2);
+  buf = buf2 = NULL;
+
+  buf = buf_new_with_capacity(5);
+  cp = "Testing. This is a moderately long Testing string.";
+  for (j = 0; cp[j]; j++)
+    write_to_buf(cp+j, 1, buf);
+  test_eq(0, buf_find_string_offset(buf, "Testing", 7));
+  test_eq(1, buf_find_string_offset(buf, "esting", 6));
+  test_eq(1, buf_find_string_offset(buf, "est", 3));
+  test_eq(39, buf_find_string_offset(buf, "ing str", 7));
+  test_eq(35, buf_find_string_offset(buf, "Testing str", 11));
+  test_eq(32, buf_find_string_offset(buf, "ng ", 3));
+  test_eq(43, buf_find_string_offset(buf, "string.", 7));
+  test_eq(-1, buf_find_string_offset(buf, "shrdlu", 6));
+  test_eq(-1, buf_find_string_offset(buf, "Testing thing", 13));
+  test_eq(-1, buf_find_string_offset(buf, "ngx", 3));
+  buf_free(buf);
+  buf = NULL;
+
+#if 0
+  {
+  int s;
+  int eof;
+  int i;
+  buf_t *buf2;
+  /****
+   * read_to_buf
+   ****/
+  s = open(get_fname("data"), O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  write(s, str, 256);
+  close(s);
+
+  s = open(get_fname("data"), O_RDONLY, 0);
+  eof = 0;
+  errno = 0; /* XXXX */
+  i = read_to_buf(s, 10, buf, &eof);
+  printf("%s\n", strerror(errno));
+  test_eq(i, 10);
+  test_eq(eof, 0);
+  //test_eq(buf_capacity(buf), 4096);
+  test_eq(buf_datalen(buf), 10);
+
+  test_memeq(str, (char*)_buf_peek_raw_buffer(buf), 10);
+
+  /* Test reading 0 bytes. */
+  i = read_to_buf(s, 0, buf, &eof);
+  //test_eq(buf_capacity(buf), 512*1024);
+  test_eq(buf_datalen(buf), 10);
+  test_eq(eof, 0);
+  test_eq(i, 0);
+
+  /* Now test when buffer is filled exactly. */
+  buf2 = buf_new_with_capacity(6);
+  i = read_to_buf(s, 6, buf2, &eof);
+  //test_eq(buf_capacity(buf2), 6);
+  test_eq(buf_datalen(buf2), 6);
+  test_eq(eof, 0);
+  test_eq(i, 6);
+  test_memeq(str+10, (char*)_buf_peek_raw_buffer(buf2), 6);
+  buf_free(buf2);
+  buf2 = NULL;
+
+  /* Now test when buffer is filled with more data to read. */
+  buf2 = buf_new_with_capacity(32);
+  i = read_to_buf(s, 128, buf2, &eof);
+  //test_eq(buf_capacity(buf2), 128);
+  test_eq(buf_datalen(buf2), 32);
+  test_eq(eof, 0);
+  test_eq(i, 32);
+  buf_free(buf2);
+  buf2 = NULL;
+
+  /* Now read to eof. */
+  test_assert(buf_capacity(buf) > 256);
+  i = read_to_buf(s, 1024, buf, &eof);
+  test_eq(i, (256-32-10-6));
+  test_eq(buf_capacity(buf), MAX_BUF_SIZE);
+  test_eq(buf_datalen(buf), 256-6-32);
+  test_memeq(str, (char*)_buf_peek_raw_buffer(buf), 10); /* XXX Check rest. */
+  test_eq(eof, 0);
+
+  i = read_to_buf(s, 1024, buf, &eof);
+  test_eq(i, 0);
+  test_eq(buf_capacity(buf), MAX_BUF_SIZE);
+  test_eq(buf_datalen(buf), 256-6-32);
+  test_eq(eof, 1);
+  }
+#endif
+
+ done:
+  if (buf)
+    buf_free(buf);
+  if (buf2)
+    buf_free(buf2);
+}
+
+/** Run unit tests for the onion handshake code. */
+static void
+test_onion_handshake(void)
+{
+  /* client-side */
+  crypto_dh_env_t *c_dh = NULL;
+  char c_buf[ONIONSKIN_CHALLENGE_LEN];
+  char c_keys[40];
+
+  /* server-side */
+  char s_buf[ONIONSKIN_REPLY_LEN];
+  char s_keys[40];
+
+  /* shared */
+  crypto_pk_env_t *pk = NULL;
+
+  pk = pk_generate(0);
+
+  /* client handshake 1. */
+  memset(c_buf, 0, ONIONSKIN_CHALLENGE_LEN);
+  test_assert(! onion_skin_create(pk, &c_dh, c_buf));
+
+  /* server handshake */
+  memset(s_buf, 0, ONIONSKIN_REPLY_LEN);
+  memset(s_keys, 0, 40);
+  test_assert(! onion_skin_server_handshake(c_buf, pk, NULL,
+                                            s_buf, s_keys, 40));
+
+  /* client handshake 2 */
+  memset(c_keys, 0, 40);
+  test_assert(! onion_skin_client_handshake(c_dh, s_buf, c_keys, 40));
+
+  if (memcmp(c_keys, s_keys, 40)) {
+    puts("Aiiiie");
+    exit(1);
+  }
+  test_memeq(c_keys, s_keys, 40);
+  memset(s_buf, 0, 40);
+  test_memneq(c_keys, s_buf, 40);
+
+ done:
+  if (c_dh)
+    crypto_dh_free(c_dh);
+  if (pk)
+    crypto_free_pk_env(pk);
+}
+
+static void
+test_circuit_timeout(void)
+{
+  /* Plan:
+   *  1. Generate 1000 samples
+   *  2. Estimate parameters
+   *  3. If difference, repeat
+   *  4. Save state
+   *  5. load state
+   *  6. Estimate parameters
+   *  7. compare differences
+   */
+  circuit_build_times_t initial;
+  circuit_build_times_t estimate;
+  circuit_build_times_t final;
+  double timeout1, timeout2;
+  or_state_t state;
+  char *msg;
+  int i, runs;
+  circuit_build_times_init(&initial);
+  circuit_build_times_init(&estimate);
+  circuit_build_times_init(&final);
+
+  memset(&state, 0, sizeof(or_state_t));
+
+  circuitbuild_running_unit_tests();
+#define timeout0 (build_time_t)(30*1000.0)
+  initial.Xm = 750;
+  circuit_build_times_initial_alpha(&initial, BUILDTIMEOUT_QUANTILE_CUTOFF,
+                                    timeout0);
+  do {
+    int n = 0;
+    for (i=0; i < MIN_CIRCUITS_TO_OBSERVE; i++) {
+      if (circuit_build_times_add_time(&estimate,
+              circuit_build_times_generate_sample(&initial, 0, 1)) == 0) {
+        n++;
+      }
+    }
+    circuit_build_times_update_alpha(&estimate);
+    timeout1 = circuit_build_times_calculate_timeout(&estimate,
+                                  BUILDTIMEOUT_QUANTILE_CUTOFF);
+    circuit_build_times_set_timeout(&estimate);
+    log_warn(LD_CIRC, "Timeout is %lf, Xm is %d", timeout1, estimate.Xm);
+    /* XXX: 5% distribution error may not be the right metric */
+  } while (fabs(circuit_build_times_cdf(&initial, timeout0) -
+                circuit_build_times_cdf(&initial, timeout1)) > 0.05
+                /* 5% error */
+           && estimate.total_build_times < NCIRCUITS_TO_OBSERVE);
+
+  test_assert(estimate.total_build_times < NCIRCUITS_TO_OBSERVE);
+
+  circuit_build_times_update_state(&estimate, &state);
+  test_assert(circuit_build_times_parse_state(&final, &state, &msg) == 0);
+
+  circuit_build_times_update_alpha(&final);
+  timeout2 = circuit_build_times_calculate_timeout(&final,
+                                 BUILDTIMEOUT_QUANTILE_CUTOFF);
+
+  circuit_build_times_set_timeout(&final);
+  log_warn(LD_CIRC, "Timeout is %lf, Xm is %d", timeout2, final.Xm);
+
+  test_assert(fabs(circuit_build_times_cdf(&initial, timeout0) -
+                   circuit_build_times_cdf(&initial, timeout2)) < 0.05);
+
+  for (runs = 0; runs < 50; runs++) {
+    int build_times_idx = 0;
+    int total_build_times = 0;
+
+    final.timeout_ms = BUILD_TIMEOUT_INITIAL_VALUE;
+    estimate.timeout_ms = BUILD_TIMEOUT_INITIAL_VALUE;
+
+    for (i = 0; i < RECENT_CIRCUITS*2; i++) {
+      circuit_build_times_network_circ_success(&estimate);
+      circuit_build_times_add_time(&estimate,
+            circuit_build_times_generate_sample(&estimate, 0,
+                BUILDTIMEOUT_QUANTILE_CUTOFF));
+      estimate.have_computed_timeout = 1;
+      circuit_build_times_network_circ_success(&estimate);
+      circuit_build_times_add_time(&final,
+            circuit_build_times_generate_sample(&final, 0,
+                BUILDTIMEOUT_QUANTILE_CUTOFF));
+      final.have_computed_timeout = 1;
+    }
+
+    test_assert(!circuit_build_times_network_check_changed(&estimate));
+    test_assert(!circuit_build_times_network_check_changed(&final));
+
+    /* Reset liveness to be non-live */
+    final.liveness.network_last_live = 0;
+    estimate.liveness.network_last_live = 0;
+
+    build_times_idx = estimate.build_times_idx;
+    total_build_times = estimate.total_build_times;
+    for (i = 0; i < NETWORK_NONLIVE_TIMEOUT_COUNT; i++) {
+      test_assert(circuit_build_times_network_check_live(&estimate));
+      test_assert(circuit_build_times_network_check_live(&final));
+
+      if (circuit_build_times_add_timeout(&estimate, 0,
+                 (time_t)(approx_time()-estimate.timeout_ms/1000.0-1)))
+        estimate.have_computed_timeout = 1;
+      if (circuit_build_times_add_timeout(&final, 0,
+                 (time_t)(approx_time()-final.timeout_ms/1000.0-1)))
+        final.have_computed_timeout = 1;
+    }
+
+    test_assert(!circuit_build_times_network_check_live(&estimate));
+    test_assert(!circuit_build_times_network_check_live(&final));
+
+    for ( ; i < NETWORK_NONLIVE_DISCARD_COUNT; i++) {
+      if (circuit_build_times_add_timeout(&estimate, 0,
+                (time_t)(approx_time()-estimate.timeout_ms/1000.0-1)))
+        estimate.have_computed_timeout = 1;
+
+      if (i < NETWORK_NONLIVE_DISCARD_COUNT-1) {
+        if (circuit_build_times_add_timeout(&final, 0,
+                (time_t)(approx_time()-final.timeout_ms/1000.0-1)))
+          final.have_computed_timeout = 1;
+      }
+    }
+
+    test_assert(!circuit_build_times_network_check_live(&estimate));
+    test_assert(!circuit_build_times_network_check_live(&final));
+
+    log_info(LD_CIRC, "idx: %d %d, tot: %d %d",
+             build_times_idx, estimate.build_times_idx,
+             total_build_times, estimate.total_build_times);
+
+    /* Check rollback index. Should match top of loop. */
+    test_assert(build_times_idx == estimate.build_times_idx);
+    test_assert(total_build_times == estimate.total_build_times);
+
+    /* Now simulate that the network has become live and we need
+     * a change */
+    circuit_build_times_network_is_live(&estimate);
+    circuit_build_times_network_is_live(&final);
+
+    for (i = 0; i < MAX_RECENT_TIMEOUT_COUNT; i++) {
+      if (circuit_build_times_add_timeout(&estimate, 1, approx_time()-1))
+        estimate.have_computed_timeout = 1;
+
+      if (i < MAX_RECENT_TIMEOUT_COUNT-1) {
+        if (circuit_build_times_add_timeout(&final, 1, approx_time()-1))
+          final.have_computed_timeout = 1;
+      }
+    }
+
+    test_assert(estimate.liveness.after_firsthop_idx == 0);
+    test_assert(final.liveness.after_firsthop_idx ==
+                MAX_RECENT_TIMEOUT_COUNT-1);
+
+    test_assert(circuit_build_times_network_check_live(&estimate));
+    test_assert(circuit_build_times_network_check_live(&final));
+
+    if (circuit_build_times_add_timeout(&final, 1, approx_time()-1))
+      final.have_computed_timeout = 1;
+
+  }
+
+done:
+  return;
+}
+
+/** Helper: Parse the exit policy string in <b>policy_str</b>, and make sure
+ * that policies_summarize() produces the string <b>expected_summary</b> from
+ * it. */
+static void
+test_policy_summary_helper(const char *policy_str,
+                           const char *expected_summary)
+{
+  config_line_t line;
+  smartlist_t *policy = smartlist_create();
+  char *summary = NULL;
+  int r;
+
+  line.key = (char*)"foo";
+  line.value = (char *)policy_str;
+  line.next = NULL;
+
+  r = policies_parse_exit_policy(&line, &policy, 0, NULL);
+  test_eq(r, 0);
+  summary = policy_summarize(policy);
+
+  test_assert(summary != NULL);
+  test_streq(summary, expected_summary);
+
+ done:
+  tor_free(summary);
+  if (policy)
+    addr_policy_list_free(policy);
+}
+
+/** Run unit tests for generating summary lines of exit policies */
+static void
+test_policies(void)
+{
+  int i;
+  smartlist_t *policy = NULL, *policy2 = NULL;
+  addr_policy_t *p;
+  tor_addr_t tar;
+  config_line_t line;
+  smartlist_t *sm = NULL;
+  char *policy_str = NULL;
+
+  policy = smartlist_create();
+
+  p = router_parse_addr_policy_item_from_string("reject 192.168.0.0/16:*",-1);
+  test_assert(p != NULL);
+  test_eq(ADDR_POLICY_REJECT, p->policy_type);
+  tor_addr_from_ipv4h(&tar, 0xc0a80000u);
+  test_eq(0, tor_addr_compare(&p->addr, &tar, CMP_EXACT));
+  test_eq(16, p->maskbits);
+  test_eq(1, p->prt_min);
+  test_eq(65535, p->prt_max);
+
+  smartlist_add(policy, p);
+
+  test_assert(ADDR_POLICY_ACCEPTED ==
+          compare_addr_to_addr_policy(0x01020304u, 2, policy));
+  test_assert(ADDR_POLICY_PROBABLY_ACCEPTED ==
+          compare_addr_to_addr_policy(0, 2, policy));
+  test_assert(ADDR_POLICY_REJECTED ==
+          compare_addr_to_addr_policy(0xc0a80102, 2, policy));
+
+  policy2 = NULL;
+  test_assert(0 == policies_parse_exit_policy(NULL, &policy2, 1, NULL));
+  test_assert(policy2);
+
+  test_assert(!exit_policy_is_general_exit(policy));
+  test_assert(exit_policy_is_general_exit(policy2));
+  test_assert(!exit_policy_is_general_exit(NULL));
+
+  test_assert(cmp_addr_policies(policy, policy2));
+  test_assert(cmp_addr_policies(policy, NULL));
+  test_assert(!cmp_addr_policies(policy2, policy2));
+  test_assert(!cmp_addr_policies(NULL, NULL));
+
+  test_assert(!policy_is_reject_star(policy2));
+  test_assert(policy_is_reject_star(policy));
+  test_assert(policy_is_reject_star(NULL));
+
+  addr_policy_list_free(policy);
+  policy = NULL;
+
+  /* make sure compacting logic works. */
+  policy = NULL;
+  line.key = (char*)"foo";
+  line.value = (char*)"accept *:80,reject private:*,reject *:*";
+  line.next = NULL;
+  test_assert(0 == policies_parse_exit_policy(&line, &policy, 0, NULL));
+  test_assert(policy);
+  //test_streq(policy->string, "accept *:80");
+  //test_streq(policy->next->string, "reject *:*");
+  test_eq(smartlist_len(policy), 2);
+
+  /* test policy summaries */
+  /* check if we properly ignore private IP addresses */
+  test_policy_summary_helper("reject 192.168.0.0/16:*,"
+                             "reject 0.0.0.0/8:*,"
+                             "reject 10.0.0.0/8:*,"
+                             "accept *:10-30,"
+                             "accept *:90,"
+                             "reject *:*",
+                             "accept 10-30,90");
+  /* check all accept policies, and proper counting of rejects */
+  test_policy_summary_helper("reject 11.0.0.0/9:80,"
+                             "reject 12.0.0.0/9:80,"
+                             "reject 13.0.0.0/9:80,"
+                             "reject 14.0.0.0/9:80,"
+                             "accept *:*", "accept 1-65535");
+  test_policy_summary_helper("reject 11.0.0.0/9:80,"
+                             "reject 12.0.0.0/9:80,"
+                             "reject 13.0.0.0/9:80,"
+                             "reject 14.0.0.0/9:80,"
+                             "reject 15.0.0.0:81,"
+                             "accept *:*", "accept 1-65535");
+  test_policy_summary_helper("reject 11.0.0.0/9:80,"
+                             "reject 12.0.0.0/9:80,"
+                             "reject 13.0.0.0/9:80,"
+                             "reject 14.0.0.0/9:80,"
+                             "reject 15.0.0.0:80,"
+                             "accept *:*",
+                             "reject 80");
+  /* no exits */
+  test_policy_summary_helper("accept 11.0.0.0/9:80,"
+                             "reject *:*",
+                             "reject 1-65535");
+  /* port merging */
+  test_policy_summary_helper("accept *:80,"
+                             "accept *:81,"
+                             "accept *:100-110,"
+                             "accept *:111,"
+                             "reject *:*",
+                             "accept 80-81,100-111");
+  /* border ports */
+  test_policy_summary_helper("accept *:1,"
+                             "accept *:3,"
+                             "accept *:65535,"
+                             "reject *:*",
+                             "accept 1,3,65535");
+  /* holes */
+  test_policy_summary_helper("accept *:1,"
+                             "accept *:3,"
+                             "accept *:5,"
+                             "accept *:7,"
+                             "reject *:*",
+                             "accept 1,3,5,7");
+  test_policy_summary_helper("reject *:1,"
+                             "reject *:3,"
+                             "reject *:5,"
+                             "reject *:7,"
+                             "accept *:*",
+                             "reject 1,3,5,7");
+
+  /* truncation ports */
+  sm = smartlist_create();
+  for (i=1; i<2000; i+=2) {
+    char buf[POLICY_BUF_LEN];
+    tor_snprintf(buf, sizeof(buf), "reject *:%d", i);
+    smartlist_add(sm, tor_strdup(buf));
+  }
+  smartlist_add(sm, tor_strdup("accept *:*"));
+  policy_str = smartlist_join_strings(sm, ",", 0, NULL);
+  test_policy_summary_helper( policy_str,
+    "accept 2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,"
+    "46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,"
+    "92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,"
+    "130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,"
+    "166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,"
+    "202,204,206,208,210,212,214,216,218,220,222,224,226,228,230,232,234,236,"
+    "238,240,242,244,246,248,250,252,254,256,258,260,262,264,266,268,270,272,"
+    "274,276,278,280,282,284,286,288,290,292,294,296,298,300,302,304,306,308,"
+    "310,312,314,316,318,320,322,324,326,328,330,332,334,336,338,340,342,344,"
+    "346,348,350,352,354,356,358,360,362,364,366,368,370,372,374,376,378,380,"
+    "382,384,386,388,390,392,394,396,398,400,402,404,406,408,410,412,414,416,"
+    "418,420,422,424,426,428,430,432,434,436,438,440,442,444,446,448,450,452,"
+    "454,456,458,460,462,464,466,468,470,472,474,476,478,480,482,484,486,488,"
+    "490,492,494,496,498,500,502,504,506,508,510,512,514,516,518,520,522");
+
+ done:
+  if (policy)
+    addr_policy_list_free(policy);
+  if (policy2)
+    addr_policy_list_free(policy2);
+  tor_free(policy_str);
+  if (sm) {
+    SMARTLIST_FOREACH(sm, char *, s, tor_free(s));
+    smartlist_free(sm);
+  }
+}
+
+/** Run AES performance benchmarks. */
+static void
+bench_aes(void)
+{
+  int len, i;
+  char *b1, *b2;
+  crypto_cipher_env_t *c;
+  struct timeval start, end;
+  const int iters = 100000;
+  uint64_t nsec;
+  c = crypto_new_cipher_env();
+  crypto_cipher_generate_key(c);
+  crypto_cipher_encrypt_init_cipher(c);
+  for (len = 1; len <= 8192; len *= 2) {
+    b1 = tor_malloc_zero(len);
+    b2 = tor_malloc_zero(len);
+    tor_gettimeofday(&start);
+    for (i = 0; i < iters; ++i) {
+      crypto_cipher_encrypt(c, b1, b2, len);
+    }
+    tor_gettimeofday(&end);
+    tor_free(b1);
+    tor_free(b2);
+    nsec = (uint64_t) tv_udiff(&start,&end);
+    nsec *= 1000;
+    nsec /= (iters*len);
+    printf("%d bytes: "U64_FORMAT" nsec per byte\n", len,
+           U64_PRINTF_ARG(nsec));
+  }
+  crypto_free_cipher_env(c);
+}
+
+/** Run digestmap_t performance benchmarks. */
+static void
+bench_dmap(void)
+{
+  smartlist_t *sl = smartlist_create();
+  smartlist_t *sl2 = smartlist_create();
+  struct timeval start, end, pt2, pt3, pt4;
+  const int iters = 10000;
+  const int elts = 4000;
+  const int fpostests = 1000000;
+  char d[20];
+  int i,n=0, fp = 0;
+  digestmap_t *dm = digestmap_new();
+  digestset_t *ds = digestset_new(elts);
+
+  for (i = 0; i < elts; ++i) {
+    crypto_rand(d, 20);
+    smartlist_add(sl, tor_memdup(d, 20));
+  }
+  for (i = 0; i < elts; ++i) {
+    crypto_rand(d, 20);
+    smartlist_add(sl2, tor_memdup(d, 20));
+  }
+  printf("nbits=%d\n", ds->mask+1);
+
+  tor_gettimeofday(&start);
+  for (i = 0; i < iters; ++i) {
+    SMARTLIST_FOREACH(sl, const char *, cp, digestmap_set(dm, cp, (void*)1));
+  }
+  tor_gettimeofday(&pt2);
+  for (i = 0; i < iters; ++i) {
+    SMARTLIST_FOREACH(sl, const char *, cp, digestmap_get(dm, cp));
+    SMARTLIST_FOREACH(sl2, const char *, cp, digestmap_get(dm, cp));
+  }
+  tor_gettimeofday(&pt3);
+  for (i = 0; i < iters; ++i) {
+    SMARTLIST_FOREACH(sl, const char *, cp, digestset_add(ds, cp));
+  }
+  tor_gettimeofday(&pt4);
+  for (i = 0; i < iters; ++i) {
+    SMARTLIST_FOREACH(sl, const char *, cp, n += digestset_isin(ds, cp));
+    SMARTLIST_FOREACH(sl2, const char *, cp, n += digestset_isin(ds, cp));
+  }
+  tor_gettimeofday(&end);
+
+  for (i = 0; i < fpostests; ++i) {
+    crypto_rand(d, 20);
+    if (digestset_isin(ds, d)) ++fp;
+  }
+
+  printf("%ld\n",(unsigned long)tv_udiff(&start, &pt2));
+  printf("%ld\n",(unsigned long)tv_udiff(&pt2, &pt3));
+  printf("%ld\n",(unsigned long)tv_udiff(&pt3, &pt4));
+  printf("%ld\n",(unsigned long)tv_udiff(&pt4, &end));
+  printf("-- %d\n", n);
+  printf("++ %f\n", fp/(double)fpostests);
+  digestmap_free(dm, NULL);
+  digestset_free(ds);
+  SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+  SMARTLIST_FOREACH(sl2, char *, cp, tor_free(cp));
+  smartlist_free(sl);
+  smartlist_free(sl2);
+}
+
+/** Test encoding and parsing of rendezvous service descriptors. */
+static void
+test_rend_fns(void)
+{
+  rend_service_descriptor_t *generated = NULL, *parsed = NULL;
+  char service_id[DIGEST_LEN];
+  char service_id_base32[REND_SERVICE_ID_LEN_BASE32+1];
+  const char *next_desc;
+  smartlist_t *descs = smartlist_create();
+  char computed_desc_id[DIGEST_LEN];
+  char parsed_desc_id[DIGEST_LEN];
+  crypto_pk_env_t *pk1 = NULL, *pk2 = NULL;
+  time_t now;
+  char *intro_points_encrypted = NULL;
+  size_t intro_points_size;
+  size_t encoded_size;
+  int i;
+  char address1[] = "fooaddress.onion";
+  char address2[] = "aaaaaaaaaaaaaaaa.onion";
+  char address3[] = "fooaddress.exit";
+  char address4[] = "www.torproject.org";
+
+  test_assert(BAD_HOSTNAME == parse_extended_hostname(address1, 1));
+  test_assert(ONION_HOSTNAME == parse_extended_hostname(address2, 1));
+  test_assert(EXIT_HOSTNAME == parse_extended_hostname(address3, 1));
+  test_assert(NORMAL_HOSTNAME == parse_extended_hostname(address4, 1));
+
+  pk1 = pk_generate(0);
+  pk2 = pk_generate(1);
+  generated = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  generated->pk = crypto_pk_dup_key(pk1);
+  crypto_pk_get_digest(generated->pk, service_id);
+  base32_encode(service_id_base32, REND_SERVICE_ID_LEN_BASE32+1,
+                service_id, REND_SERVICE_ID_LEN);
+  now = time(NULL);
+  generated->timestamp = now;
+  generated->version = 2;
+  generated->protocols = 42;
+  generated->intro_nodes = smartlist_create();
+
+  for (i = 0; i < 3; i++) {
+    rend_intro_point_t *intro = tor_malloc_zero(sizeof(rend_intro_point_t));
+    crypto_pk_env_t *okey = pk_generate(2 + i);
+    intro->extend_info = tor_malloc_zero(sizeof(extend_info_t));
+    intro->extend_info->onion_key = okey;
+    crypto_pk_get_digest(intro->extend_info->onion_key,
+                         intro->extend_info->identity_digest);
+    //crypto_rand(info->identity_digest, DIGEST_LEN); /* Would this work? */
+    intro->extend_info->nickname[0] = '$';
+    base16_encode(intro->extend_info->nickname + 1,
+                  sizeof(intro->extend_info->nickname) - 1,
+                  intro->extend_info->identity_digest, DIGEST_LEN);
+    /* Does not cover all IP addresses. */
+    tor_addr_from_ipv4h(&intro->extend_info->addr, crypto_rand_int(65536));
+    intro->extend_info->port = crypto_rand_int(65536);
+    intro->intro_key = crypto_pk_dup_key(pk2);
+    smartlist_add(generated->intro_nodes, intro);
+  }
+  test_assert(rend_encode_v2_descriptors(descs, generated, now, 0,
+                                         REND_NO_AUTH, NULL, NULL) > 0);
+  test_assert(rend_compute_v2_desc_id(computed_desc_id, service_id_base32,
+                                      NULL, now, 0) == 0);
+  test_memeq(((rend_encoded_v2_service_descriptor_t *)
+             smartlist_get(descs, 0))->desc_id, computed_desc_id, DIGEST_LEN);
+  test_assert(rend_parse_v2_service_descriptor(&parsed, parsed_desc_id,
+                                               &intro_points_encrypted,
+                                               &intro_points_size,
+                                               &encoded_size,
+                                               &next_desc,
+                                     ((rend_encoded_v2_service_descriptor_t *)
+                                     smartlist_get(descs, 0))->desc_str) == 0);
+  test_assert(parsed);
+  test_memeq(((rend_encoded_v2_service_descriptor_t *)
+             smartlist_get(descs, 0))->desc_id, parsed_desc_id, DIGEST_LEN);
+  test_eq(rend_parse_introduction_points(parsed, intro_points_encrypted,
+                                         intro_points_size), 3);
+  test_assert(!crypto_pk_cmp_keys(generated->pk, parsed->pk));
+  test_eq(parsed->timestamp, now);
+  test_eq(parsed->version, 2);
+  test_eq(parsed->protocols, 42);
+  test_eq(smartlist_len(parsed->intro_nodes), 3);
+  for (i = 0; i < smartlist_len(parsed->intro_nodes); i++) {
+    rend_intro_point_t *par_intro = smartlist_get(parsed->intro_nodes, i),
+      *gen_intro = smartlist_get(generated->intro_nodes, i);
+    extend_info_t *par_info = par_intro->extend_info;
+    extend_info_t *gen_info = gen_intro->extend_info;
+    test_assert(!crypto_pk_cmp_keys(gen_info->onion_key, par_info->onion_key));
+    test_memeq(gen_info->identity_digest, par_info->identity_digest,
+               DIGEST_LEN);
+    test_streq(gen_info->nickname, par_info->nickname);
+    test_assert(tor_addr_eq(&gen_info->addr, &par_info->addr));
+    test_eq(gen_info->port, par_info->port);
+  }
+
+  rend_service_descriptor_free(parsed);
+  rend_service_descriptor_free(generated);
+  parsed = generated = NULL;
+
+ done:
+  if (descs) {
+    for (i = 0; i < smartlist_len(descs); i++)
+      rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
+    smartlist_free(descs);
+  }
+  if (parsed)
+    rend_service_descriptor_free(parsed);
+  if (generated)
+    rend_service_descriptor_free(generated);
+  if (pk1)
+    crypto_free_pk_env(pk1);
+  if (pk2)
+    crypto_free_pk_env(pk2);
+  tor_free(intro_points_encrypted);
+}
+
+/** Run unit tests for GeoIP code. */
+static void
+test_geoip(void)
+{
+  int i, j;
+  time_t now = time(NULL);
+  char *s = NULL;
+
+  /* Populate the DB a bit.  Add these in order, since we can't do the final
+   * 'sort' step.  These aren't very good IP addresses, but they're perfectly
+   * fine uint32_t values. */
+  test_eq(0, geoip_parse_entry("10,50,AB"));
+  test_eq(0, geoip_parse_entry("52,90,XY"));
+  test_eq(0, geoip_parse_entry("95,100,AB"));
+  test_eq(0, geoip_parse_entry("\"105\",\"140\",\"ZZ\""));
+  test_eq(0, geoip_parse_entry("\"150\",\"190\",\"XY\""));
+  test_eq(0, geoip_parse_entry("\"200\",\"250\",\"AB\""));
+
+  /* We should have 3 countries: ab, xy, zz. */
+  test_eq(3, geoip_get_n_countries());
+  /* Make sure that country ID actually works. */
+#define NAMEFOR(x) geoip_get_country_name(geoip_get_country_by_ip(x))
+  test_streq("ab", NAMEFOR(32));
+  test_streq("??", NAMEFOR(5));
+  test_streq("??", NAMEFOR(51));
+  test_streq("xy", NAMEFOR(150));
+  test_streq("xy", NAMEFOR(190));
+  test_streq("??", NAMEFOR(2000));
+#undef NAMEFOR
+
+  get_options()->BridgeRelay = 1;
+  get_options()->BridgeRecordUsageByCountry = 1;
+  /* Put 9 observations in AB... */
+  for (i=32; i < 40; ++i)
+    geoip_note_client_seen(GEOIP_CLIENT_CONNECT, i, now-7200);
+  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, 225, now-7200);
+  /* and 3 observations in XY, several times. */
+  for (j=0; j < 10; ++j)
+    for (i=52; i < 55; ++i)
+      geoip_note_client_seen(GEOIP_CLIENT_CONNECT, i, now-3600);
+  /* and 17 observations in ZZ... */
+  for (i=110; i < 127; ++i)
+    geoip_note_client_seen(GEOIP_CLIENT_CONNECT, i, now);
+  s = geoip_get_client_history_bridge(now+5*24*60*60,
+                                      GEOIP_CLIENT_CONNECT);
+  test_assert(s);
+  test_streq("zz=24,ab=16,xy=8", s);
+  tor_free(s);
+
+  /* Now clear out all the AB observations. */
+  geoip_remove_old_clients(now-6000);
+  s = geoip_get_client_history_bridge(now+5*24*60*60,
+                                      GEOIP_CLIENT_CONNECT);
+  test_assert(s);
+  test_streq("zz=24,xy=8", s);
+
+ done:
+  tor_free(s);
+}
+
+static void *
+legacy_test_setup(const struct testcase_t *testcase)
+{
+  return testcase->setup_data;
+}
+
+void
+legacy_test_helper(void *data)
+{
+  void (*fn)(void) = data;
+  fn();
+}
+
+static int
+legacy_test_cleanup(const struct testcase_t *testcase, void *ptr)
+{
+  (void)ptr;
+  (void)testcase;
+  return 1;
+}
+
+const struct testcase_setup_t legacy_setup = {
+  legacy_test_setup, legacy_test_cleanup
+};
+
+#define ENT(name)                                                       \
+  { #name, legacy_test_helper, 0, &legacy_setup, test_ ## name }
+#define SUBENT(group, name)                                             \
+  { #group "_" #name, legacy_test_helper, 0, &legacy_setup,             \
+      test_ ## group ## _ ## name }
+#define DISABLED(name)                                                  \
+  { #name, legacy_test_helper, TT_SKIP, &legacy_setup, name }
+
+static struct testcase_t test_array[] = {
+  ENT(buffers),
+  ENT(onion_handshake),
+  ENT(circuit_timeout),
+  ENT(policies),
+  ENT(rend_fns),
+  ENT(geoip),
+
+  DISABLED(bench_aes),
+  DISABLED(bench_dmap),
+  END_OF_TESTCASES
+};
+
+extern struct testcase_t addr_tests[];
+extern struct testcase_t crypto_tests[];
+extern struct testcase_t container_tests[];
+extern struct testcase_t util_tests[];
+extern struct testcase_t dir_tests[];
+
+static struct testgroup_t testgroups[] = {
+  { "", test_array },
+  { "addr/", addr_tests },
+  { "crypto/", crypto_tests },
+  { "container/", container_tests },
+  { "util/", util_tests },
+  { "dir/", dir_tests },
+  END_OF_GROUPS
+};
+
+/** Main entry point for unit test code: parse the command line, and run
+ * some unit tests. */
+int
+main(int c, const char **v)
+{
+  or_options_t *options;
+  char *errmsg = NULL;
+  int i, i_out;
+  int loglevel = LOG_ERR;
+
+#ifdef USE_DMALLOC
+  {
+    int r = CRYPTO_set_mem_ex_functions(_tor_malloc, _tor_realloc, _tor_free);
+    tor_assert(r);
+  }
+#endif
+
+  update_approx_time(time(NULL));
+  options = options_new();
+  tor_threads_init();
+  init_logging();
+
+  for (i_out = i = 1; i < c; ++i) {
+    if (!strcmp(v[i], "--warn")) {
+      loglevel = LOG_WARN;
+    } else if (!strcmp(v[i], "--notice")) {
+      loglevel = LOG_NOTICE;
+    } else if (!strcmp(v[i], "--info")) {
+      loglevel = LOG_INFO;
+    } else if (!strcmp(v[i], "--debug")) {
+      loglevel = LOG_DEBUG;
+    } else {
+      v[i_out++] = v[i];
+    }
+  }
+  c = i_out;
+
+  {
+    log_severity_list_t s;
+    memset(&s, 0, sizeof(s));
+    set_log_severity_config(loglevel, LOG_ERR, &s);
+    add_stream_log(&s, "", fileno(stdout));
+  }
+
+  options->command = CMD_RUN_UNITTESTS;
+  crypto_global_init(0, NULL, NULL);
+  rep_hist_init();
+  network_init();
+  setup_directory();
+  options_init(options);
+  options->DataDirectory = tor_strdup(temp_dir);
+  options->EntryStatistics = 1;
+  if (set_options(options, &errmsg) < 0) {
+    printf("Failed to set initial options: %s\n", errmsg);
+    tor_free(errmsg);
+    return 1;
+  }
+
+  crypto_seed_rng(1);
+
+  atexit(remove_directory);
+
+  have_failed = (tinytest_main(c, v, testgroups) < 0);
+
+  free_pregenerated_keys();
+#ifdef USE_DMALLOC
+  tor_free_all(0);
+  dmalloc_log_unfreed();
+#endif
+
+  if (have_failed)
+    return 1;
+  else
+    return 0;
+}
+


Property changes on: tor/trunk/src/test/test.c
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Added: tor/trunk/src/test/test.h
===================================================================
--- tor/trunk/src/test/test.h	                        (rev 0)
+++ tor/trunk/src/test/test.h	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,72 @@
+/* Copyright (c) 2001-2003, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2009, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef _TOR_TEST_H
+#define _TOR_TEST_H
+
+/**
+ * \file test.h
+ * \brief Macros and functions used by unit tests.
+ */
+
+#include "compat.h"
+#include "tinytest.h"
+#define TT_EXIT_TEST_FUNCTION STMT_BEGIN goto done; STMT_END
+#include "tinytest_macros.h"
+
+#ifdef __GNUC__
+#define PRETTY_FUNCTION __PRETTY_FUNCTION__
+#else
+#define PRETTY_FUNCTION ""
+#endif
+
+#define test_fail_msg(msg) TT_DIE((msg))
+
+#define test_fail() test_fail_msg("Assertion failed.")
+
+#define test_assert(expr) tt_assert(expr)
+
+#define test_eq(expr1, expr2) tt_int_op((expr1), ==, (expr2))
+#define test_eq_ptr(expr1, expr2) tt_ptr_op((expr1), ==, (expr2))
+#define test_neq(expr1, expr2) tt_int_op((expr1), !=, (expr2))
+#define test_neq_ptr(expr1, expr2) tt_ptr_op((expr1), !=, (expr2))
+#define test_streq(expr1, expr2) tt_str_op((expr1), ==, (expr2))
+#define test_strneq(expr1, expr2) tt_str_op((expr1), !=, (expr2))
+#define test_streq(expr1, expr2) tt_str_op((expr1), ==, (expr2))
+
+#define test_mem_op(expr1, op, expr2, len)                              \
+  tt_assert_test_fmt_type(expr1,expr2,#expr1" "#op" "#expr2,            \
+                          const char *,                                 \
+                          (memcmp(_val1, _val2, len) op 0),             \
+                          char *, "%s",                                 \
+                          { size_t printlen = (len)*2+1;                \
+                            _print = tor_malloc(printlen);              \
+                            base16_encode(_print, printlen, _value,     \
+                                          (len)); },                    \
+                          { tor_free(_print); }                         \
+                          );
+
+#define test_memeq(expr1, expr2, len) test_mem_op((expr1), ==, (expr2), len)
+#define test_memneq(expr1, expr2, len) test_mem_op((expr1), !=, (expr2), len)
+
+#define test_mem_op_hex(expr1, op, hex)                                 \
+  STMT_BEGIN                                                            \
+  size_t length = strlen(hex);                                          \
+  char *value2 = tor_malloc(length/2);                                  \
+  tor_assert((length&1)==0);                                            \
+  base16_decode(value2, length/2, hex, length);                         \
+  test_mem_op(expr1, op, value2, length/2);                             \
+  STMT_END
+
+#define test_memeq_hex(expr1, hex) test_mem_op_hex(expr1, ==, hex)
+
+const char *get_fname(const char *name);
+crypto_pk_env_t *pk_generate(int idx);
+
+void legacy_test_helper(void *data);
+extern const struct testcase_setup_t legacy_setup;
+
+#endif
+


Property changes on: tor/trunk/src/test/test.h
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Added: tor/trunk/src/test/test_addr.c
===================================================================
--- tor/trunk/src/test/test_addr.c	                        (rev 0)
+++ tor/trunk/src/test/test_addr.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,497 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2009, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "or.h"
+#include "test.h"
+
+static void
+test_addr_basic(void)
+{
+  uint32_t u32;
+  uint16_t u16;
+  char *cp;
+
+  /* Test parse_addr_port */
+  cp = NULL; u32 = 3; u16 = 3;
+  test_assert(!parse_addr_port(LOG_WARN, "1.2.3.4", &cp, &u32, &u16));
+  test_streq(cp, "1.2.3.4");
+  test_eq(u32, 0x01020304u);
+  test_eq(u16, 0);
+  tor_free(cp);
+  test_assert(!parse_addr_port(LOG_WARN, "4.3.2.1:99", &cp, &u32, &u16));
+  test_streq(cp, "4.3.2.1");
+  test_eq(u32, 0x04030201u);
+  test_eq(u16, 99);
+  tor_free(cp);
+  test_assert(!parse_addr_port(LOG_WARN, "nonexistent.address:4040",
+                               &cp, NULL, &u16));
+  test_streq(cp, "nonexistent.address");
+  test_eq(u16, 4040);
+  tor_free(cp);
+  test_assert(!parse_addr_port(LOG_WARN, "localhost:9999", &cp, &u32, &u16));
+  test_streq(cp, "localhost");
+  test_eq(u32, 0x7f000001u);
+  test_eq(u16, 9999);
+  tor_free(cp);
+  u32 = 3;
+  test_assert(!parse_addr_port(LOG_WARN, "localhost", NULL, &u32, &u16));
+  test_eq(cp, NULL);
+  test_eq(u32, 0x7f000001u);
+  test_eq(u16, 0);
+  tor_free(cp);
+  test_eq(0, addr_mask_get_bits(0x0u));
+  test_eq(32, addr_mask_get_bits(0xFFFFFFFFu));
+  test_eq(16, addr_mask_get_bits(0xFFFF0000u));
+  test_eq(31, addr_mask_get_bits(0xFFFFFFFEu));
+  test_eq(1, addr_mask_get_bits(0x80000000u));
+
+  /* Test inet_ntop */
+  {
+    char tmpbuf[TOR_ADDR_BUF_LEN];
+    const char *ip = "176.192.208.224";
+    struct in_addr in;
+    tor_inet_pton(AF_INET, ip, &in);
+    tor_inet_ntop(AF_INET, &in, tmpbuf, sizeof(tmpbuf));
+    test_streq(tmpbuf, ip);
+  }
+
+ done:
+  ;
+}
+
+#define _test_op_ip6(a,op,b,e1,e2)                               \
+  STMT_BEGIN                                                     \
+  tt_assert_test_fmt_type(a,b,e1" "#op" "e2,struct in6_addr*,    \
+    (memcmp(_val1->s6_addr, _val2->s6_addr, 16) op 0),           \
+    char *, "%s",                                                \
+    { int i; char *cp;                                           \
+      cp = _print = tor_malloc(64);                              \
+      for (i=0;i<16;++i) {                                       \
+        tor_snprintf(cp, 3,"%02x", (unsigned)_value->s6_addr[i]);\
+        cp += 2;                                                 \
+        if (i != 15) *cp++ = ':';                                \
+      }                                                          \
+    }, { tor_free(_print); }                                     \
+  );                                                             \
+  STMT_END
+
+/** Helper: Assert that two strings both decode as IPv6 addresses with
+ * tor_inet_pton(), and both decode to the same address. */
+#define test_pton6_same(a,b) STMT_BEGIN                \
+     test_eq(tor_inet_pton(AF_INET6, a, &a1), 1);      \
+     test_eq(tor_inet_pton(AF_INET6, b, &a2), 1);      \
+     _test_op_ip6(&a1,==,&a2,#a,#b);                   \
+  STMT_END
+
+/** Helper: Assert that <b>a</b> is recognized as a bad IPv6 address by
+ * tor_inet_pton(). */
+#define test_pton6_bad(a)                       \
+  test_eq(0, tor_inet_pton(AF_INET6, a, &a1))
+
+/** Helper: assert that <b>a</b>, when parsed by tor_inet_pton() and displayed
+ * with tor_inet_ntop(), yields <b>b</b>. Also assert that <b>b</b> parses to
+ * the same value as <b>a</b>. */
+#define test_ntop6_reduces(a,b) STMT_BEGIN                              \
+    test_eq(tor_inet_pton(AF_INET6, a, &a1), 1);                        \
+    test_streq(tor_inet_ntop(AF_INET6, &a1, buf, sizeof(buf)), b);      \
+    test_eq(tor_inet_pton(AF_INET6, b, &a2), 1);                        \
+    _test_op_ip6(&a1, ==, &a2, a, b);                                   \
+  STMT_END
+
+/** Helper: assert that <b>a</b> parses by tor_inet_pton() into a address that
+ * passes tor_addr_is_internal() with <b>for_listening</b>. */
+#define test_internal_ip(a,for_listening) STMT_BEGIN           \
+    test_eq(tor_inet_pton(AF_INET6, a, &t1.addr.in6_addr), 1); \
+    t1.family = AF_INET6;                                      \
+    if (!tor_addr_is_internal(&t1, for_listening))             \
+      test_fail_msg( a "was not internal.");                   \
+  STMT_END
+
+/** Helper: assert that <b>a</b> parses by tor_inet_pton() into a address that
+ * does not pass tor_addr_is_internal() with <b>for_listening</b>. */
+#define test_external_ip(a,for_listening) STMT_BEGIN           \
+    test_eq(tor_inet_pton(AF_INET6, a, &t1.addr.in6_addr), 1); \
+    t1.family = AF_INET6;                                      \
+    if (tor_addr_is_internal(&t1, for_listening))              \
+      test_fail_msg(a  "was not external.");                   \
+  STMT_END
+
+/** Helper: Assert that <b>a</b> and <b>b</b>, when parsed by
+ * tor_inet_pton(), give addresses that compare in the order defined by
+ * <b>op</b> with tor_addr_compare(). */
+#define test_addr_compare(a, op, b) STMT_BEGIN                    \
+    test_eq(tor_inet_pton(AF_INET6, a, &t1.addr.in6_addr), 1);    \
+    test_eq(tor_inet_pton(AF_INET6, b, &t2.addr.in6_addr), 1);    \
+    t1.family = t2.family = AF_INET6;                             \
+    r = tor_addr_compare(&t1,&t2,CMP_SEMANTIC);                   \
+    if (!(r op 0))                                                \
+      test_fail_msg("failed: tor_addr_compare("a","b") "#op" 0"); \
+  STMT_END
+
+/** Helper: Assert that <b>a</b> and <b>b</b>, when parsed by
+ * tor_inet_pton(), give addresses that compare in the order defined by
+ * <b>op</b> with tor_addr_compare_masked() with <b>m</b> masked. */
+#define test_addr_compare_masked(a, op, b, m) STMT_BEGIN          \
+    test_eq(tor_inet_pton(AF_INET6, a, &t1.addr.in6_addr), 1);    \
+    test_eq(tor_inet_pton(AF_INET6, b, &t2.addr.in6_addr), 1);    \
+    t1.family = t2.family = AF_INET6;                             \
+    r = tor_addr_compare_masked(&t1,&t2,m,CMP_SEMANTIC);          \
+    if (!(r op 0))                                                \
+      test_fail_msg("failed: tor_addr_compare_masked("a","b","#m") "#op" 0"); \
+  STMT_END
+
+/** Helper: assert that <b>xx</b> is parseable as a masked IPv6 address with
+ * ports by tor_parse_mask_addr_ports(), with family <b>f</b>, IP address
+ * as 4 32-bit words <b>ip1...ip4</b>, mask bits as <b>mm</b>, and port range
+ * as <b>pt1..pt2</b>. */
+#define test_addr_mask_ports_parse(xx, f, ip1, ip2, ip3, ip4, mm, pt1, pt2) \
+  STMT_BEGIN                                                                \
+    test_eq(tor_addr_parse_mask_ports(xx, &t1, &mask, &port1, &port2), f);  \
+    p1=tor_inet_ntop(AF_INET6, &t1.addr.in6_addr, bug, sizeof(bug));        \
+    test_eq(htonl(ip1), tor_addr_to_in6_addr32(&t1)[0]);            \
+    test_eq(htonl(ip2), tor_addr_to_in6_addr32(&t1)[1]);            \
+    test_eq(htonl(ip3), tor_addr_to_in6_addr32(&t1)[2]);            \
+    test_eq(htonl(ip4), tor_addr_to_in6_addr32(&t1)[3]);            \
+    test_eq(mask, mm);                                     \
+    test_eq(port1, pt1);                                   \
+    test_eq(port2, pt2);                                   \
+  STMT_END
+
+/** Run unit tests for IPv6 encoding/decoding/manipulation functions. */
+static void
+test_addr_ip6_helpers(void)
+{
+  char buf[TOR_ADDR_BUF_LEN], bug[TOR_ADDR_BUF_LEN];
+  struct in6_addr a1, a2;
+  tor_addr_t t1, t2;
+  int r, i;
+  uint16_t port1, port2;
+  maskbits_t mask;
+  const char *p1;
+  struct sockaddr_storage sa_storage;
+  struct sockaddr_in *sin;
+  struct sockaddr_in6 *sin6;
+
+  //  struct in_addr b1, b2;
+  /* Test tor_inet_ntop and tor_inet_pton: IPv6 */
+
+  /* ==== Converting to and from sockaddr_t. */
+  sin = (struct sockaddr_in *)&sa_storage;
+  sin->sin_family = AF_INET;
+  sin->sin_port = 9090;
+  sin->sin_addr.s_addr = htonl(0x7f7f0102); /*127.127.1.2*/
+  tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin, NULL);
+  test_eq(tor_addr_family(&t1), AF_INET);
+  test_eq(tor_addr_to_ipv4h(&t1), 0x7f7f0102);
+
+  memset(&sa_storage, 0, sizeof(sa_storage));
+  test_eq(sizeof(struct sockaddr_in),
+          tor_addr_to_sockaddr(&t1, 1234, (struct sockaddr *)&sa_storage,
+                               sizeof(sa_storage)));
+  test_eq(1234, ntohs(sin->sin_port));
+  test_eq(0x7f7f0102, ntohl(sin->sin_addr.s_addr));
+
+  memset(&sa_storage, 0, sizeof(sa_storage));
+  sin6 = (struct sockaddr_in6 *)&sa_storage;
+  sin6->sin6_family = AF_INET6;
+  sin6->sin6_port = htons(7070);
+  sin6->sin6_addr.s6_addr[0] = 128;
+  tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin6, NULL);
+  test_eq(tor_addr_family(&t1), AF_INET6);
+  p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 0);
+  test_streq(p1, "8000::");
+
+  memset(&sa_storage, 0, sizeof(sa_storage));
+  test_eq(sizeof(struct sockaddr_in6),
+          tor_addr_to_sockaddr(&t1, 9999, (struct sockaddr *)&sa_storage,
+                               sizeof(sa_storage)));
+  test_eq(AF_INET6, sin6->sin6_family);
+  test_eq(9999, ntohs(sin6->sin6_port));
+  test_eq(0x80000000, ntohl(S6_ADDR32(sin6->sin6_addr)[0]));
+
+  /* ==== tor_addr_lookup: static cases.  (Can't test dns without knowing we
+   * have a good resolver. */
+  test_eq(0, tor_addr_lookup("127.128.129.130", AF_UNSPEC, &t1));
+  test_eq(AF_INET, tor_addr_family(&t1));
+  test_eq(tor_addr_to_ipv4h(&t1), 0x7f808182);
+
+  test_eq(0, tor_addr_lookup("9000::5", AF_UNSPEC, &t1));
+  test_eq(AF_INET6, tor_addr_family(&t1));
+  test_eq(0x90, tor_addr_to_in6_addr8(&t1)[0]);
+  test_assert(tor_mem_is_zero((char*)tor_addr_to_in6_addr8(&t1)+1, 14));
+  test_eq(0x05, tor_addr_to_in6_addr8(&t1)[15]);
+
+  /* === Test pton: valid af_inet6 */
+  /* Simple, valid parsing. */
+  r = tor_inet_pton(AF_INET6,
+                    "0102:0304:0506:0708:090A:0B0C:0D0E:0F10", &a1);
+  test_assert(r==1);
+  for (i=0;i<16;++i) { test_eq(i+1, (int)a1.s6_addr[i]); }
+  /* ipv4 ending. */
+  test_pton6_same("0102:0304:0506:0708:090A:0B0C:0D0E:0F10",
+                  "0102:0304:0506:0708:090A:0B0C:13.14.15.16");
+  /* shortened words. */
+  test_pton6_same("0001:0099:BEEF:0000:0123:FFFF:0001:0001",
+                  "1:99:BEEF:0:0123:FFFF:1:1");
+  /* zeros at the beginning */
+  test_pton6_same("0000:0000:0000:0000:0009:C0A8:0001:0001",
+                  "::9:c0a8:1:1");
+  test_pton6_same("0000:0000:0000:0000:0009:C0A8:0001:0001",
+                  "::9:c0a8:0.1.0.1");
+  /* zeros in the middle. */
+  test_pton6_same("fe80:0000:0000:0000:0202:1111:0001:0001",
+                  "fe80::202:1111:1:1");
+  /* zeros at the end. */
+  test_pton6_same("1000:0001:0000:0007:0000:0000:0000:0000",
+                  "1000:1:0:7::");
+
+  /* === Test ntop: af_inet6 */
+  test_ntop6_reduces("0:0:0:0:0:0:0:0", "::");
+
+  test_ntop6_reduces("0001:0099:BEEF:0006:0123:FFFF:0001:0001",
+                     "1:99:beef:6:123:ffff:1:1");
+
+  //test_ntop6_reduces("0:0:0:0:0:0:c0a8:0101", "::192.168.1.1");
+  test_ntop6_reduces("0:0:0:0:0:ffff:c0a8:0101", "::ffff:192.168.1.1");
+  test_ntop6_reduces("002:0:0000:0:3::4", "2::3:0:0:4");
+  test_ntop6_reduces("0:0::1:0:3", "::1:0:3");
+  test_ntop6_reduces("008:0::0", "8::");
+  test_ntop6_reduces("0:0:0:0:0:ffff::1", "::ffff:0.0.0.1");
+  test_ntop6_reduces("abcd:0:0:0:0:0:7f00::", "abcd::7f00:0");
+  test_ntop6_reduces("0000:0000:0000:0000:0009:C0A8:0001:0001",
+                     "::9:c0a8:1:1");
+  test_ntop6_reduces("fe80:0000:0000:0000:0202:1111:0001:0001",
+                     "fe80::202:1111:1:1");
+  test_ntop6_reduces("1000:0001:0000:0007:0000:0000:0000:0000",
+                     "1000:1:0:7::");
+
+  /* === Test pton: invalid in6. */
+  test_pton6_bad("foobar.");
+  test_pton6_bad("55555::");
+  test_pton6_bad("9:-60::");
+  test_pton6_bad("1:2:33333:4:0002:3::");
+  //test_pton6_bad("1:2:3333:4:00002:3::");// BAD, but glibc doesn't say so.
+  test_pton6_bad("1:2:3333:4:fish:3::");
+  test_pton6_bad("1:2:3:4:5:6:7:8:9");
+  test_pton6_bad("1:2:3:4:5:6:7");
+  test_pton6_bad("1:2:3:4:5:6:1.2.3.4.5");
+  test_pton6_bad("1:2:3:4:5:6:1.2.3");
+  test_pton6_bad("::1.2.3");
+  test_pton6_bad("::1.2.3.4.5");
+  test_pton6_bad("99");
+  test_pton6_bad("");
+  test_pton6_bad("1::2::3:4");
+  test_pton6_bad("a:::b:c");
+  test_pton6_bad(":::a:b:c");
+  test_pton6_bad("a:b:c:::");
+
+  /* test internal checking */
+  test_external_ip("fbff:ffff::2:7", 0);
+  test_internal_ip("fc01::2:7", 0);
+  test_internal_ip("fdff:ffff::f:f", 0);
+  test_external_ip("fe00::3:f", 0);
+
+  test_external_ip("fe7f:ffff::2:7", 0);
+  test_internal_ip("fe80::2:7", 0);
+  test_internal_ip("febf:ffff::f:f", 0);
+
+  test_internal_ip("fec0::2:7:7", 0);
+  test_internal_ip("feff:ffff::e:7:7", 0);
+  test_external_ip("ff00::e:7:7", 0);
+
+  test_internal_ip("::", 0);
+  test_internal_ip("::1", 0);
+  test_internal_ip("::1", 1);
+  test_internal_ip("::", 0);
+  test_external_ip("::", 1);
+  test_external_ip("::2", 0);
+  test_external_ip("2001::", 0);
+  test_external_ip("ffff::", 0);
+
+  test_external_ip("::ffff:0.0.0.0", 1);
+  test_internal_ip("::ffff:0.0.0.0", 0);
+  test_internal_ip("::ffff:0.255.255.255", 0);
+  test_external_ip("::ffff:1.0.0.0", 0);
+
+  test_external_ip("::ffff:9.255.255.255", 0);
+  test_internal_ip("::ffff:10.0.0.0", 0);
+  test_internal_ip("::ffff:10.255.255.255", 0);
+  test_external_ip("::ffff:11.0.0.0", 0);
+
+  test_external_ip("::ffff:126.255.255.255", 0);
+  test_internal_ip("::ffff:127.0.0.0", 0);
+  test_internal_ip("::ffff:127.255.255.255", 0);
+  test_external_ip("::ffff:128.0.0.0", 0);
+
+  test_external_ip("::ffff:172.15.255.255", 0);
+  test_internal_ip("::ffff:172.16.0.0", 0);
+  test_internal_ip("::ffff:172.31.255.255", 0);
+  test_external_ip("::ffff:172.32.0.0", 0);
+
+  test_external_ip("::ffff:192.167.255.255", 0);
+  test_internal_ip("::ffff:192.168.0.0", 0);
+  test_internal_ip("::ffff:192.168.255.255", 0);
+  test_external_ip("::ffff:192.169.0.0", 0);
+
+  test_external_ip("::ffff:169.253.255.255", 0);
+  test_internal_ip("::ffff:169.254.0.0", 0);
+  test_internal_ip("::ffff:169.254.255.255", 0);
+  test_external_ip("::ffff:169.255.0.0", 0);
+  test_assert(is_internal_IP(0x7f000001, 0));
+
+  /* tor_addr_compare(tor_addr_t x2) */
+  test_addr_compare("ffff::", ==, "ffff::0");
+  test_addr_compare("0::3:2:1", <, "0::ffff:0.3.2.1");
+  test_addr_compare("0::2:2:1", <, "0::ffff:0.3.2.1");
+  test_addr_compare("0::ffff:0.3.2.1", >, "0::0:0:0");
+  test_addr_compare("0::ffff:5.2.2.1", <, "::ffff:6.0.0.0"); /* XXXX wrong. */
+  tor_addr_parse_mask_ports("[::ffff:2.3.4.5]", &t1, NULL, NULL, NULL);
+  tor_addr_parse_mask_ports("2.3.4.5", &t2, NULL, NULL, NULL);
+  test_assert(tor_addr_compare(&t1, &t2, CMP_SEMANTIC) == 0);
+  tor_addr_parse_mask_ports("[::ffff:2.3.4.4]", &t1, NULL, NULL, NULL);
+  tor_addr_parse_mask_ports("2.3.4.5", &t2, NULL, NULL, NULL);
+  test_assert(tor_addr_compare(&t1, &t2, CMP_SEMANTIC) < 0);
+
+  /* test compare_masked */
+  test_addr_compare_masked("ffff::", ==, "ffff::0", 128);
+  test_addr_compare_masked("ffff::", ==, "ffff::0", 64);
+  test_addr_compare_masked("0::2:2:1", <, "0::8000:2:1", 81);
+  test_addr_compare_masked("0::2:2:1", ==, "0::8000:2:1", 80);
+
+  /* Test decorated addr_to_string. */
+  test_eq(AF_INET6, tor_addr_from_str(&t1, "[123:45:6789::5005:11]"));
+  p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 1);
+  test_streq(p1, "[123:45:6789::5005:11]");
+  test_eq(AF_INET, tor_addr_from_str(&t1, "18.0.0.1"));
+  p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 1);
+  test_streq(p1, "18.0.0.1");
+
+  /* Test tor_addr_parse_reverse_lookup_name */
+  i = tor_addr_parse_reverse_lookup_name(&t1, "Foobar.baz", AF_UNSPEC, 0);
+  test_eq(0, i);
+  i = tor_addr_parse_reverse_lookup_name(&t1, "Foobar.baz", AF_UNSPEC, 1);
+  test_eq(0, i);
+  i = tor_addr_parse_reverse_lookup_name(&t1, "1.0.168.192.in-addr.arpa",
+                                         AF_UNSPEC, 1);
+  test_eq(1, i);
+  test_eq(tor_addr_family(&t1), AF_INET);
+  p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 1);
+  test_streq(p1, "192.168.0.1");
+  i = tor_addr_parse_reverse_lookup_name(&t1, "192.168.0.99", AF_UNSPEC, 0);
+  test_eq(0, i);
+  i = tor_addr_parse_reverse_lookup_name(&t1, "192.168.0.99", AF_UNSPEC, 1);
+  test_eq(1, i);
+  p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 1);
+  test_streq(p1, "192.168.0.99");
+  memset(&t1, 0, sizeof(t1));
+  i = tor_addr_parse_reverse_lookup_name(&t1,
+                                         "0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f."
+                                         "f.e.e.b.1.e.b.e.e.f.f.e.e.e.d.9."
+                                         "ip6.ARPA",
+                                         AF_UNSPEC, 0);
+  test_eq(1, i);
+  p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 1);
+  test_streq(p1, "[9dee:effe:ebe1:beef:fedc:ba98:7654:3210]");
+  /* Failing cases. */
+  i = tor_addr_parse_reverse_lookup_name(&t1,
+                                         "6.7.8.9.a.b.c.d.e.f."
+                                         "f.e.e.b.1.e.b.e.e.f.f.e.e.e.d.9."
+                                         "ip6.ARPA",
+                                         AF_UNSPEC, 0);
+  test_eq(i, -1);
+  i = tor_addr_parse_reverse_lookup_name(&t1,
+                                         "6.7.8.9.a.b.c.d.e.f.a.b.c.d.e.f.0."
+                                         "f.e.e.b.1.e.b.e.e.f.f.e.e.e.d.9."
+                                         "ip6.ARPA",
+                                         AF_UNSPEC, 0);
+  test_eq(i, -1);
+  i = tor_addr_parse_reverse_lookup_name(&t1,
+                                         "6.7.8.9.a.b.c.d.e.f.X.0.0.0.0.9."
+                                         "f.e.e.b.1.e.b.e.e.f.f.e.e.e.d.9."
+                                         "ip6.ARPA",
+                                         AF_UNSPEC, 0);
+  test_eq(i, -1);
+  i = tor_addr_parse_reverse_lookup_name(&t1, "32.1.1.in-addr.arpa",
+                                         AF_UNSPEC, 0);
+  test_eq(i, -1);
+  i = tor_addr_parse_reverse_lookup_name(&t1, ".in-addr.arpa",
+                                         AF_UNSPEC, 0);
+  test_eq(i, -1);
+  i = tor_addr_parse_reverse_lookup_name(&t1, "1.2.3.4.5.in-addr.arpa",
+                                         AF_UNSPEC, 0);
+  test_eq(i, -1);
+  i = tor_addr_parse_reverse_lookup_name(&t1, "1.2.3.4.5.in-addr.arpa",
+                                         AF_INET6, 0);
+  test_eq(i, -1);
+  i = tor_addr_parse_reverse_lookup_name(&t1,
+                                         "6.7.8.9.a.b.c.d.e.f.a.b.c.d.e.0."
+                                         "f.e.e.b.1.e.b.e.e.f.f.e.e.e.d.9."
+                                         "ip6.ARPA",
+                                         AF_INET, 0);
+  test_eq(i, -1);
+
+  /* test tor_addr_parse_mask_ports */
+  test_addr_mask_ports_parse("[::f]/17:47-95", AF_INET6,
+                             0, 0, 0, 0x0000000f, 17, 47, 95);
+  //test_addr_parse("[::fefe:4.1.1.7/120]:999-1000");
+  //test_addr_parse_check("::fefe:401:107", 120, 999, 1000);
+  test_addr_mask_ports_parse("[::ffff:4.1.1.7]/120:443", AF_INET6,
+                             0, 0, 0x0000ffff, 0x04010107, 120, 443, 443);
+  test_addr_mask_ports_parse("[abcd:2::44a:0]:2-65000", AF_INET6,
+                             0xabcd0002, 0, 0, 0x044a0000, 128, 2, 65000);
+
+  r=tor_addr_parse_mask_ports("[fefef::]/112", &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("efef::/112", &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("[f:f:f:f:f:f:f:f::]", &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("[::f:f:f:f:f:f:f:f]", &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("[f:f:f:f:f:f:f:f:f]", &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  /* Test for V4-mapped address with mask < 96.  (arguably not valid) */
+  r=tor_addr_parse_mask_ports("[::ffff:1.1.2.2/33]", &t1, &mask, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("1.1.2.2/33", &t1, &mask, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("1.1.2.2/31", &t1, &mask, NULL, NULL);
+  test_assert(r == AF_INET);
+  r=tor_addr_parse_mask_ports("[efef::]/112", &t1, &mask, &port1, &port2);
+  test_assert(r == AF_INET6);
+  test_assert(port1 == 1);
+  test_assert(port2 == 65535);
+
+  /* make sure inet address lengths >= max */
+  test_assert(INET_NTOA_BUF_LEN >= sizeof("255.255.255.255"));
+  test_assert(TOR_ADDR_BUF_LEN >=
+              sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"));
+
+  test_assert(sizeof(tor_addr_t) >= sizeof(struct in6_addr));
+
+  /* get interface addresses */
+  r = get_interface_address6(LOG_DEBUG, AF_INET, &t1);
+  i = get_interface_address6(LOG_DEBUG, AF_INET6, &t2);
+#if 0
+  tor_inet_ntop(AF_INET, &t1.sa.sin_addr, buf, sizeof(buf));
+  printf("\nv4 address: %s  (family=%i)", buf, IN_FAMILY(&t1));
+  tor_inet_ntop(AF_INET6, &t2.sa6.sin6_addr, buf, sizeof(buf));
+  printf("\nv6 address: %s  (family=%i)", buf, IN_FAMILY(&t2));
+#endif
+
+ done:
+  ;
+}
+
+#define ADDR_LEGACY(name)                                               \
+  { #name, legacy_test_helper, 0, &legacy_setup, test_addr_ ## name }
+
+struct testcase_t addr_tests[] = {
+  ADDR_LEGACY(basic),
+  ADDR_LEGACY(ip6_helpers),
+  END_OF_TESTCASES
+};
+


Property changes on: tor/trunk/src/test/test_addr.c
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Added: tor/trunk/src/test/test_containers.c
===================================================================
--- tor/trunk/src/test/test_containers.c	                        (rev 0)
+++ tor/trunk/src/test/test_containers.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,719 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2009, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "or.h"
+#include "test.h"
+
+/** Helper: return a tristate based on comparing the strings in *<b>a</b> and
+ * *<b>b</b>. */
+static int
+_compare_strs(const void **a, const void **b)
+{
+  const char *s1 = *a, *s2 = *b;
+  return strcmp(s1, s2);
+}
+
+/** Helper: return a tristate based on comparing the strings in *<b>a</b> and
+ * *<b>b</b>, excluding a's first character, and ignoring case. */
+static int
+_compare_without_first_ch(const void *a, const void **b)
+{
+  const char *s1 = a, *s2 = *b;
+  return strcasecmp(s1+1, s2);
+}
+
+/** Run unit tests for basic dynamic-sized array functionality. */
+static void
+test_container_smartlist_basic(void)
+{
+  smartlist_t *sl;
+
+  /* XXXX test sort_digests, uniq_strings, uniq_digests */
+
+  /* Test smartlist add, del_keeporder, insert, get. */
+  sl = smartlist_create();
+  smartlist_add(sl, (void*)1);
+  smartlist_add(sl, (void*)2);
+  smartlist_add(sl, (void*)3);
+  smartlist_add(sl, (void*)4);
+  smartlist_del_keeporder(sl, 1);
+  smartlist_insert(sl, 1, (void*)22);
+  smartlist_insert(sl, 0, (void*)0);
+  smartlist_insert(sl, 5, (void*)555);
+  test_eq_ptr((void*)0,   smartlist_get(sl,0));
+  test_eq_ptr((void*)1,   smartlist_get(sl,1));
+  test_eq_ptr((void*)22,  smartlist_get(sl,2));
+  test_eq_ptr((void*)3,   smartlist_get(sl,3));
+  test_eq_ptr((void*)4,   smartlist_get(sl,4));
+  test_eq_ptr((void*)555, smartlist_get(sl,5));
+  /* Try deleting in the middle. */
+  smartlist_del(sl, 1);
+  test_eq_ptr((void*)555, smartlist_get(sl, 1));
+  /* Try deleting at the end. */
+  smartlist_del(sl, 4);
+  test_eq(4, smartlist_len(sl));
+
+  /* test isin. */
+  test_assert(smartlist_isin(sl, (void*)3));
+  test_assert(!smartlist_isin(sl, (void*)99));
+
+ done:
+  smartlist_free(sl);
+}
+
+/** Run unit tests for smartlist-of-strings functionality. */
+static void
+test_container_smartlist_strings(void)
+{
+  smartlist_t *sl = smartlist_create();
+  char *cp=NULL, *cp_alloc=NULL;
+  size_t sz;
+
+  /* Test split and join */
+  test_eq(0, smartlist_len(sl));
+  smartlist_split_string(sl, "abc", ":", 0, 0);
+  test_eq(1, smartlist_len(sl));
+  test_streq("abc", smartlist_get(sl, 0));
+  smartlist_split_string(sl, "a::bc::", "::", 0, 0);
+  test_eq(4, smartlist_len(sl));
+  test_streq("a", smartlist_get(sl, 1));
+  test_streq("bc", smartlist_get(sl, 2));
+  test_streq("", smartlist_get(sl, 3));
+  cp_alloc = smartlist_join_strings(sl, "", 0, NULL);
+  test_streq(cp_alloc, "abcabc");
+  tor_free(cp_alloc);
+  cp_alloc = smartlist_join_strings(sl, "!", 0, NULL);
+  test_streq(cp_alloc, "abc!a!bc!");
+  tor_free(cp_alloc);
+  cp_alloc = smartlist_join_strings(sl, "XY", 0, NULL);
+  test_streq(cp_alloc, "abcXYaXYbcXY");
+  tor_free(cp_alloc);
+  cp_alloc = smartlist_join_strings(sl, "XY", 1, NULL);
+  test_streq(cp_alloc, "abcXYaXYbcXYXY");
+  tor_free(cp_alloc);
+  cp_alloc = smartlist_join_strings(sl, "", 1, NULL);
+  test_streq(cp_alloc, "abcabc");
+  tor_free(cp_alloc);
+
+  smartlist_split_string(sl, "/def/  /ghijk", "/", 0, 0);
+  test_eq(8, smartlist_len(sl));
+  test_streq("", smartlist_get(sl, 4));
+  test_streq("def", smartlist_get(sl, 5));
+  test_streq("  ", smartlist_get(sl, 6));
+  test_streq("ghijk", smartlist_get(sl, 7));
+  SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+  smartlist_clear(sl);
+
+  smartlist_split_string(sl, "a,bbd,cdef", ",", SPLIT_SKIP_SPACE, 0);
+  test_eq(3, smartlist_len(sl));
+  test_streq("a", smartlist_get(sl,0));
+  test_streq("bbd", smartlist_get(sl,1));
+  test_streq("cdef", smartlist_get(sl,2));
+  smartlist_split_string(sl, " z <> zhasd <>  <> bnud<>   ", "<>",
+                         SPLIT_SKIP_SPACE, 0);
+  test_eq(8, smartlist_len(sl));
+  test_streq("z", smartlist_get(sl,3));
+  test_streq("zhasd", smartlist_get(sl,4));
+  test_streq("", smartlist_get(sl,5));
+  test_streq("bnud", smartlist_get(sl,6));
+  test_streq("", smartlist_get(sl,7));
+
+  SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+  smartlist_clear(sl);
+
+  smartlist_split_string(sl, " ab\tc \td ef  ", NULL,
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+  test_eq(4, smartlist_len(sl));
+  test_streq("ab", smartlist_get(sl,0));
+  test_streq("c", smartlist_get(sl,1));
+  test_streq("d", smartlist_get(sl,2));
+  test_streq("ef", smartlist_get(sl,3));
+  smartlist_split_string(sl, "ghi\tj", NULL,
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+  test_eq(6, smartlist_len(sl));
+  test_streq("ghi", smartlist_get(sl,4));
+  test_streq("j", smartlist_get(sl,5));
+
+  SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+  smartlist_clear(sl);
+
+  cp_alloc = smartlist_join_strings(sl, "XY", 0, NULL);
+  test_streq(cp_alloc, "");
+  tor_free(cp_alloc);
+  cp_alloc = smartlist_join_strings(sl, "XY", 1, NULL);
+  test_streq(cp_alloc, "XY");
+  tor_free(cp_alloc);
+
+  smartlist_split_string(sl, " z <> zhasd <>  <> bnud<>   ", "<>",
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+  test_eq(3, smartlist_len(sl));
+  test_streq("z", smartlist_get(sl, 0));
+  test_streq("zhasd", smartlist_get(sl, 1));
+  test_streq("bnud", smartlist_get(sl, 2));
+  smartlist_split_string(sl, " z <> zhasd <>  <> bnud<>   ", "<>",
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2);
+  test_eq(5, smartlist_len(sl));
+  test_streq("z", smartlist_get(sl, 3));
+  test_streq("zhasd <>  <> bnud<>", smartlist_get(sl, 4));
+  SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+  smartlist_clear(sl);
+
+  smartlist_split_string(sl, "abcd\n", "\n",
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+  test_eq(1, smartlist_len(sl));
+  test_streq("abcd", smartlist_get(sl, 0));
+  smartlist_split_string(sl, "efgh", "\n",
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+  test_eq(2, smartlist_len(sl));
+  test_streq("efgh", smartlist_get(sl, 1));
+
+  SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+  smartlist_clear(sl);
+
+  /* Test swapping, shuffling, and sorting. */
+  smartlist_split_string(sl, "the,onion,router,by,arma,and,nickm", ",", 0, 0);
+  test_eq(7, smartlist_len(sl));
+  smartlist_sort(sl, _compare_strs);
+  cp_alloc = smartlist_join_strings(sl, ",", 0, NULL);
+  test_streq(cp_alloc,"and,arma,by,nickm,onion,router,the");
+  tor_free(cp_alloc);
+  smartlist_swap(sl, 1, 5);
+  cp_alloc = smartlist_join_strings(sl, ",", 0, NULL);
+  test_streq(cp_alloc,"and,router,by,nickm,onion,arma,the");
+  tor_free(cp_alloc);
+  smartlist_shuffle(sl);
+  test_eq(7, smartlist_len(sl));
+  test_assert(smartlist_string_isin(sl, "and"));
+  test_assert(smartlist_string_isin(sl, "router"));
+  test_assert(smartlist_string_isin(sl, "by"));
+  test_assert(smartlist_string_isin(sl, "nickm"));
+  test_assert(smartlist_string_isin(sl, "onion"));
+  test_assert(smartlist_string_isin(sl, "arma"));
+  test_assert(smartlist_string_isin(sl, "the"));
+
+  /* Test bsearch. */
+  smartlist_sort(sl, _compare_strs);
+  test_streq("nickm", smartlist_bsearch(sl, "zNicKM",
+                                        _compare_without_first_ch));
+  test_streq("and", smartlist_bsearch(sl, " AND", _compare_without_first_ch));
+  test_eq_ptr(NULL, smartlist_bsearch(sl, " ANz", _compare_without_first_ch));
+
+  /* Test bsearch_idx */
+  {
+    int f;
+    test_eq(0, smartlist_bsearch_idx(sl," aaa",_compare_without_first_ch,&f));
+    test_eq(f, 0);
+    test_eq(0, smartlist_bsearch_idx(sl," and",_compare_without_first_ch,&f));
+    test_eq(f, 1);
+    test_eq(1, smartlist_bsearch_idx(sl," arm",_compare_without_first_ch,&f));
+    test_eq(f, 0);
+    test_eq(1, smartlist_bsearch_idx(sl," arma",_compare_without_first_ch,&f));
+    test_eq(f, 1);
+    test_eq(2, smartlist_bsearch_idx(sl," armb",_compare_without_first_ch,&f));
+    test_eq(f, 0);
+    test_eq(7, smartlist_bsearch_idx(sl," zzzz",_compare_without_first_ch,&f));
+    test_eq(f, 0);
+  }
+
+  /* Test reverse() and pop_last() */
+  smartlist_reverse(sl);
+  cp_alloc = smartlist_join_strings(sl, ",", 0, NULL);
+  test_streq(cp_alloc,"the,router,onion,nickm,by,arma,and");
+  tor_free(cp_alloc);
+  cp_alloc = smartlist_pop_last(sl);
+  test_streq(cp_alloc, "and");
+  tor_free(cp_alloc);
+  test_eq(smartlist_len(sl), 6);
+  SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+  smartlist_clear(sl);
+  cp_alloc = smartlist_pop_last(sl);
+  test_eq(cp_alloc, NULL);
+
+  /* Test uniq() */
+  smartlist_split_string(sl,
+                     "50,noon,radar,a,man,a,plan,a,canal,panama,radar,noon,50",
+                     ",", 0, 0);
+  smartlist_sort(sl, _compare_strs);
+  smartlist_uniq(sl, _compare_strs, _tor_free);
+  cp_alloc = smartlist_join_strings(sl, ",", 0, NULL);
+  test_streq(cp_alloc, "50,a,canal,man,noon,panama,plan,radar");
+  tor_free(cp_alloc);
+
+  /* Test string_isin and isin_case and num_isin */
+  test_assert(smartlist_string_isin(sl, "noon"));
+  test_assert(!smartlist_string_isin(sl, "noonoon"));
+  test_assert(smartlist_string_isin_case(sl, "nOOn"));
+  test_assert(!smartlist_string_isin_case(sl, "nooNooN"));
+  test_assert(smartlist_string_num_isin(sl, 50));
+  test_assert(!smartlist_string_num_isin(sl, 60));
+
+  /* Test smartlist_choose */
+  {
+    int i;
+    int allsame = 1;
+    int allin = 1;
+    void *first = smartlist_choose(sl);
+    test_assert(smartlist_isin(sl, first));
+    for (i = 0; i < 100; ++i) {
+      void *second = smartlist_choose(sl);
+      if (second != first)
+        allsame = 0;
+      if (!smartlist_isin(sl, second))
+        allin = 0;
+    }
+    test_assert(!allsame);
+    test_assert(allin);
+  }
+  SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+  smartlist_clear(sl);
+
+  /* Test string_remove and remove and join_strings2 */
+  smartlist_split_string(sl,
+                    "Some say the Earth will end in ice and some in fire",
+                    " ", 0, 0);
+  cp = smartlist_get(sl, 4);
+  test_streq(cp, "will");
+  smartlist_add(sl, cp);
+  smartlist_remove(sl, cp);
+  tor_free(cp);
+  cp_alloc = smartlist_join_strings(sl, ",", 0, NULL);
+  test_streq(cp_alloc, "Some,say,the,Earth,fire,end,in,ice,and,some,in");
+  tor_free(cp_alloc);
+  smartlist_string_remove(sl, "in");
+  cp_alloc = smartlist_join_strings2(sl, "+XX", 1, 0, &sz);
+  test_streq(cp_alloc, "Some+say+the+Earth+fire+end+some+ice+and");
+  test_eq((int)sz, 40);
+
+ done:
+
+  SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+  smartlist_free(sl);
+  tor_free(cp_alloc);
+}
+
+/** Run unit tests for smartlist set manipulation functions. */
+static void
+test_container_smartlist_overlap(void)
+{
+  smartlist_t *sl = smartlist_create();
+  smartlist_t *ints = smartlist_create();
+  smartlist_t *odds = smartlist_create();
+  smartlist_t *evens = smartlist_create();
+  smartlist_t *primes = smartlist_create();
+  int i;
+  for (i=1; i < 10; i += 2)
+    smartlist_add(odds, (void*)(uintptr_t)i);
+  for (i=0; i < 10; i += 2)
+    smartlist_add(evens, (void*)(uintptr_t)i);
+
+  /* add_all */
+  smartlist_add_all(ints, odds);
+  smartlist_add_all(ints, evens);
+  test_eq(smartlist_len(ints), 10);
+
+  smartlist_add(primes, (void*)2);
+  smartlist_add(primes, (void*)3);
+  smartlist_add(primes, (void*)5);
+  smartlist_add(primes, (void*)7);
+
+  /* overlap */
+  test_assert(smartlist_overlap(ints, odds));
+  test_assert(smartlist_overlap(odds, primes));
+  test_assert(smartlist_overlap(evens, primes));
+  test_assert(!smartlist_overlap(odds, evens));
+
+  /* intersect */
+  smartlist_add_all(sl, odds);
+  smartlist_intersect(sl, primes);
+  test_eq(smartlist_len(sl), 3);
+  test_assert(smartlist_isin(sl, (void*)3));
+  test_assert(smartlist_isin(sl, (void*)5));
+  test_assert(smartlist_isin(sl, (void*)7));
+
+  /* subtract */
+  smartlist_add_all(sl, primes);
+  smartlist_subtract(sl, odds);
+  test_eq(smartlist_len(sl), 1);
+  test_assert(smartlist_isin(sl, (void*)2));
+
+ done:
+  smartlist_free(odds);
+  smartlist_free(evens);
+  smartlist_free(ints);
+  smartlist_free(primes);
+  smartlist_free(sl);
+}
+
+/** Run unit tests for smartlist-of-digests functions. */
+static void
+test_container_smartlist_digests(void)
+{
+  smartlist_t *sl = smartlist_create();
+
+  /* digest_isin. */
+  smartlist_add(sl, tor_memdup("AAAAAAAAAAAAAAAAAAAA", DIGEST_LEN));
+  smartlist_add(sl, tor_memdup("\00090AAB2AAAAaasdAAAAA", DIGEST_LEN));
+  smartlist_add(sl, tor_memdup("\00090AAB2AAAAaasdAAAAA", DIGEST_LEN));
+  test_eq(0, smartlist_digest_isin(NULL, "AAAAAAAAAAAAAAAAAAAA"));
+  test_assert(smartlist_digest_isin(sl, "AAAAAAAAAAAAAAAAAAAA"));
+  test_assert(smartlist_digest_isin(sl, "\00090AAB2AAAAaasdAAAAA"));
+  test_eq(0, smartlist_digest_isin(sl, "\00090AAB2AAABaasdAAAAA"));
+
+  /* sort digests */
+  smartlist_sort_digests(sl);
+  test_memeq(smartlist_get(sl, 0), "\00090AAB2AAAAaasdAAAAA", DIGEST_LEN);
+  test_memeq(smartlist_get(sl, 1), "\00090AAB2AAAAaasdAAAAA", DIGEST_LEN);
+  test_memeq(smartlist_get(sl, 2), "AAAAAAAAAAAAAAAAAAAA", DIGEST_LEN);
+  test_eq(3, smartlist_len(sl));
+
+  /* uniq_digests */
+  smartlist_uniq_digests(sl);
+  test_eq(2, smartlist_len(sl));
+  test_memeq(smartlist_get(sl, 0), "\00090AAB2AAAAaasdAAAAA", DIGEST_LEN);
+  test_memeq(smartlist_get(sl, 1), "AAAAAAAAAAAAAAAAAAAA", DIGEST_LEN);
+
+ done:
+  SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+  smartlist_free(sl);
+}
+
+/** Run unit tests for concatenate-a-smartlist-of-strings functions. */
+static void
+test_container_smartlist_join(void)
+{
+  smartlist_t *sl = smartlist_create();
+  smartlist_t *sl2 = smartlist_create(), *sl3 = smartlist_create(),
+    *sl4 = smartlist_create();
+  char *joined=NULL;
+  /* unique, sorted. */
+  smartlist_split_string(sl,
+                         "Abashments Ambush Anchorman Bacon Banks Borscht "
+                         "Bunks Inhumane Insurance Knish Know Manners "
+                         "Maraschinos Stamina Sunbonnets Unicorns Wombats",
+                         " ", 0, 0);
+  /* non-unique, sorted. */
+  smartlist_split_string(sl2,
+                         "Ambush Anchorman Anchorman Anemias Anemias Bacon "
+                         "Crossbowmen Inhumane Insurance Knish Know Manners "
+                         "Manners Maraschinos Wombats Wombats Work",
+                         " ", 0, 0);
+  SMARTLIST_FOREACH_JOIN(sl, char *, cp1,
+                         sl2, char *, cp2,
+                         strcmp(cp1,cp2),
+                         smartlist_add(sl3, cp2)) {
+    test_streq(cp1, cp2);
+    smartlist_add(sl4, cp1);
+  } SMARTLIST_FOREACH_JOIN_END(cp1, cp2);
+
+  SMARTLIST_FOREACH(sl3, const char *, cp,
+                    test_assert(smartlist_isin(sl2, cp) &&
+                                !smartlist_string_isin(sl, cp)));
+  SMARTLIST_FOREACH(sl4, const char *, cp,
+                    test_assert(smartlist_isin(sl, cp) &&
+                                smartlist_string_isin(sl2, cp)));
+  joined = smartlist_join_strings(sl3, ",", 0, NULL);
+  test_streq(joined, "Anemias,Anemias,Crossbowmen,Work");
+  tor_free(joined);
+  joined = smartlist_join_strings(sl4, ",", 0, NULL);
+  test_streq(joined, "Ambush,Anchorman,Anchorman,Bacon,Inhumane,Insurance,"
+             "Knish,Know,Manners,Manners,Maraschinos,Wombats,Wombats");
+  tor_free(joined);
+
+ done:
+  smartlist_free(sl4);
+  smartlist_free(sl3);
+  SMARTLIST_FOREACH(sl2, char *, cp, tor_free(cp));
+  smartlist_free(sl2);
+  SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+  smartlist_free(sl);
+  tor_free(joined);
+}
+
+/** Run unit tests for bitarray code */
+static void
+test_container_bitarray(void)
+{
+  bitarray_t *ba = NULL;
+  int i, j, ok=1;
+
+  ba = bitarray_init_zero(1);
+  test_assert(ba);
+  test_assert(! bitarray_is_set(ba, 0));
+  bitarray_set(ba, 0);
+  test_assert(bitarray_is_set(ba, 0));
+  bitarray_clear(ba, 0);
+  test_assert(! bitarray_is_set(ba, 0));
+  bitarray_free(ba);
+
+  ba = bitarray_init_zero(1023);
+  for (i = 1; i < 64; ) {
+    for (j = 0; j < 1023; ++j) {
+      if (j % i)
+        bitarray_set(ba, j);
+      else
+        bitarray_clear(ba, j);
+    }
+    for (j = 0; j < 1023; ++j) {
+      if (!bool_eq(bitarray_is_set(ba, j), j%i))
+        ok = 0;
+    }
+    test_assert(ok);
+    if (i < 7)
+      ++i;
+    else if (i == 28)
+      i = 32;
+    else
+      i += 7;
+  }
+
+ done:
+  if (ba)
+    bitarray_free(ba);
+}
+
+/** Run unit tests for digest set code (implemented as a hashtable or as a
+ * bloom filter) */
+static void
+test_container_digestset(void)
+{
+  smartlist_t *included = smartlist_create();
+  char d[DIGEST_LEN];
+  int i;
+  int ok = 1;
+  int false_positives = 0;
+  digestset_t *set = NULL;
+
+  for (i = 0; i < 1000; ++i) {
+    crypto_rand(d, DIGEST_LEN);
+    smartlist_add(included, tor_memdup(d, DIGEST_LEN));
+  }
+  set = digestset_new(1000);
+  SMARTLIST_FOREACH(included, const char *, cp,
+                    if (digestset_isin(set, cp))
+                      ok = 0);
+  test_assert(ok);
+  SMARTLIST_FOREACH(included, const char *, cp,
+                    digestset_add(set, cp));
+  SMARTLIST_FOREACH(included, const char *, cp,
+                    if (!digestset_isin(set, cp))
+                      ok = 0);
+  test_assert(ok);
+  for (i = 0; i < 1000; ++i) {
+    crypto_rand(d, DIGEST_LEN);
+    if (digestset_isin(set, d))
+      ++false_positives;
+  }
+  test_assert(false_positives < 50); /* Should be far lower. */
+
+ done:
+  if (set)
+    digestset_free(set);
+  SMARTLIST_FOREACH(included, char *, cp, tor_free(cp));
+  smartlist_free(included);
+}
+
+/** Helper: return a tristate based on comparing two strings. */
+static int
+_compare_strings_for_pqueue(const void *s1, const void *s2)
+{
+  return strcmp((const char*)s1, (const char*)s2);
+}
+
+/** Run unit tests for heap-based priority queue functions. */
+static void
+test_container_pqueue(void)
+{
+  smartlist_t *sl = smartlist_create();
+  int (*cmp)(const void *, const void*);
+#define OK() smartlist_pqueue_assert_ok(sl, cmp)
+
+  cmp = _compare_strings_for_pqueue;
+
+  smartlist_pqueue_add(sl, cmp, (char*)"cows");
+  smartlist_pqueue_add(sl, cmp, (char*)"zebras");
+  smartlist_pqueue_add(sl, cmp, (char*)"fish");
+  smartlist_pqueue_add(sl, cmp, (char*)"frogs");
+  smartlist_pqueue_add(sl, cmp, (char*)"apples");
+  smartlist_pqueue_add(sl, cmp, (char*)"squid");
+  smartlist_pqueue_add(sl, cmp, (char*)"daschunds");
+  smartlist_pqueue_add(sl, cmp, (char*)"eggplants");
+  smartlist_pqueue_add(sl, cmp, (char*)"weissbier");
+  smartlist_pqueue_add(sl, cmp, (char*)"lobsters");
+  smartlist_pqueue_add(sl, cmp, (char*)"roquefort");
+
+  OK();
+
+  test_eq(smartlist_len(sl), 11);
+  test_streq(smartlist_get(sl, 0), "apples");
+  test_streq(smartlist_pqueue_pop(sl, cmp), "apples");
+  test_eq(smartlist_len(sl), 10);
+  OK();
+  test_streq(smartlist_pqueue_pop(sl, cmp), "cows");
+  test_streq(smartlist_pqueue_pop(sl, cmp), "daschunds");
+  smartlist_pqueue_add(sl, cmp, (char*)"chinchillas");
+  OK();
+  smartlist_pqueue_add(sl, cmp, (char*)"fireflies");
+  OK();
+  test_streq(smartlist_pqueue_pop(sl, cmp), "chinchillas");
+  test_streq(smartlist_pqueue_pop(sl, cmp), "eggplants");
+  test_streq(smartlist_pqueue_pop(sl, cmp), "fireflies");
+  OK();
+  test_streq(smartlist_pqueue_pop(sl, cmp), "fish");
+  test_streq(smartlist_pqueue_pop(sl, cmp), "frogs");
+  test_streq(smartlist_pqueue_pop(sl, cmp), "lobsters");
+  test_streq(smartlist_pqueue_pop(sl, cmp), "roquefort");
+  OK();
+  test_eq(smartlist_len(sl), 3);
+  test_streq(smartlist_pqueue_pop(sl, cmp), "squid");
+  test_streq(smartlist_pqueue_pop(sl, cmp), "weissbier");
+  test_streq(smartlist_pqueue_pop(sl, cmp), "zebras");
+  test_eq(smartlist_len(sl), 0);
+  OK();
+#undef OK
+
+ done:
+
+  smartlist_free(sl);
+}
+
+/** Run unit tests for string-to-void* map functions */
+static void
+test_container_strmap(void)
+{
+  strmap_t *map;
+  strmap_iter_t *iter;
+  const char *k;
+  void *v;
+  char *visited = NULL;
+  smartlist_t *found_keys = NULL;
+
+  map = strmap_new();
+  test_assert(map);
+  test_eq(strmap_size(map), 0);
+  test_assert(strmap_isempty(map));
+  v = strmap_set(map, "K1", (void*)99);
+  test_eq(v, NULL);
+  test_assert(!strmap_isempty(map));
+  v = strmap_set(map, "K2", (void*)101);
+  test_eq(v, NULL);
+  v = strmap_set(map, "K1", (void*)100);
+  test_eq(v, (void*)99);
+  test_eq_ptr(strmap_get(map,"K1"), (void*)100);
+  test_eq_ptr(strmap_get(map,"K2"), (void*)101);
+  test_eq_ptr(strmap_get(map,"K-not-there"), NULL);
+  strmap_assert_ok(map);
+
+  v = strmap_remove(map,"K2");
+  strmap_assert_ok(map);
+  test_eq_ptr(v, (void*)101);
+  test_eq_ptr(strmap_get(map,"K2"), NULL);
+  test_eq_ptr(strmap_remove(map,"K2"), NULL);
+
+  strmap_set(map, "K2", (void*)101);
+  strmap_set(map, "K3", (void*)102);
+  strmap_set(map, "K4", (void*)103);
+  test_eq(strmap_size(map), 4);
+  strmap_assert_ok(map);
+  strmap_set(map, "K5", (void*)104);
+  strmap_set(map, "K6", (void*)105);
+  strmap_assert_ok(map);
+
+  /* Test iterator. */
+  iter = strmap_iter_init(map);
+  found_keys = smartlist_create();
+  while (!strmap_iter_done(iter)) {
+    strmap_iter_get(iter,&k,&v);
+    smartlist_add(found_keys, tor_strdup(k));
+    test_eq_ptr(v, strmap_get(map, k));
+
+    if (!strcmp(k, "K2")) {
+      iter = strmap_iter_next_rmv(map,iter);
+    } else {
+      iter = strmap_iter_next(map,iter);
+    }
+  }
+
+  /* Make sure we removed K2, but not the others. */
+  test_eq_ptr(strmap_get(map, "K2"), NULL);
+  test_eq_ptr(strmap_get(map, "K5"), (void*)104);
+  /* Make sure we visited everyone once */
+  smartlist_sort_strings(found_keys);
+  visited = smartlist_join_strings(found_keys, ":", 0, NULL);
+  test_streq(visited, "K1:K2:K3:K4:K5:K6");
+
+  strmap_assert_ok(map);
+  /* Clean up after ourselves. */
+  strmap_free(map, NULL);
+  map = NULL;
+
+  /* Now try some lc functions. */
+  map = strmap_new();
+  strmap_set_lc(map,"Ab.C", (void*)1);
+  test_eq_ptr(strmap_get(map,"ab.c"), (void*)1);
+  strmap_assert_ok(map);
+  test_eq_ptr(strmap_get_lc(map,"AB.C"), (void*)1);
+  test_eq_ptr(strmap_get(map,"AB.C"), NULL);
+  test_eq_ptr(strmap_remove_lc(map,"aB.C"), (void*)1);
+  strmap_assert_ok(map);
+  test_eq_ptr(strmap_get_lc(map,"AB.C"), NULL);
+
+ done:
+  if (map)
+    strmap_free(map,NULL);
+  if (found_keys) {
+    SMARTLIST_FOREACH(found_keys, char *, cp, tor_free(cp));
+    smartlist_free(found_keys);
+  }
+  tor_free(visited);
+}
+
+/** Run unit tests for getting the median of a list. */
+static void
+test_container_order_functions(void)
+{
+  int lst[25], n = 0;
+  //  int a=12,b=24,c=25,d=60,e=77;
+
+#define median() median_int(lst, n)
+
+  lst[n++] = 12;
+  test_eq(12, median()); /* 12 */
+  lst[n++] = 77;
+  //smartlist_shuffle(sl);
+  test_eq(12, median()); /* 12, 77 */
+  lst[n++] = 77;
+  //smartlist_shuffle(sl);
+  test_eq(77, median()); /* 12, 77, 77 */
+  lst[n++] = 24;
+  test_eq(24, median()); /* 12,24,77,77 */
+  lst[n++] = 60;
+  lst[n++] = 12;
+  lst[n++] = 25;
+  //smartlist_shuffle(sl);
+  test_eq(25, median()); /* 12,12,24,25,60,77,77 */
+#undef median
+
+ done:
+  ;
+}
+
+#define CONTAINER_LEGACY(name)                                          \
+  { #name, legacy_test_helper, 0, &legacy_setup, test_container_ ## name }
+
+struct testcase_t container_tests[] = {
+  CONTAINER_LEGACY(smartlist_basic),
+  CONTAINER_LEGACY(smartlist_strings),
+  CONTAINER_LEGACY(smartlist_overlap),
+  CONTAINER_LEGACY(smartlist_digests),
+  CONTAINER_LEGACY(smartlist_join),
+  CONTAINER_LEGACY(bitarray),
+  CONTAINER_LEGACY(digestset),
+  CONTAINER_LEGACY(strmap),
+  CONTAINER_LEGACY(pqueue),
+  CONTAINER_LEGACY(order_functions),
+  END_OF_TESTCASES
+};
+


Property changes on: tor/trunk/src/test/test_containers.c
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Added: tor/trunk/src/test/test_crypto.c
===================================================================
--- tor/trunk/src/test/test_crypto.c	                        (rev 0)
+++ tor/trunk/src/test/test_crypto.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,768 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2009, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#define CRYPTO_PRIVATE
+#include "or.h"
+#include "test.h"
+
+/** Run unit tests for Diffie-Hellman functionality. */
+static void
+test_crypto_dh(void)
+{
+  crypto_dh_env_t *dh1 = crypto_dh_new();
+  crypto_dh_env_t *dh2 = crypto_dh_new();
+  char p1[DH_BYTES];
+  char p2[DH_BYTES];
+  char s1[DH_BYTES];
+  char s2[DH_BYTES];
+  ssize_t s1len, s2len;
+
+  test_eq(crypto_dh_get_bytes(dh1), DH_BYTES);
+  test_eq(crypto_dh_get_bytes(dh2), DH_BYTES);
+
+  memset(p1, 0, DH_BYTES);
+  memset(p2, 0, DH_BYTES);
+  test_memeq(p1, p2, DH_BYTES);
+  test_assert(! crypto_dh_get_public(dh1, p1, DH_BYTES));
+  test_memneq(p1, p2, DH_BYTES);
+  test_assert(! crypto_dh_get_public(dh2, p2, DH_BYTES));
+  test_memneq(p1, p2, DH_BYTES);
+
+  memset(s1, 0, DH_BYTES);
+  memset(s2, 0xFF, DH_BYTES);
+  s1len = crypto_dh_compute_secret(dh1, p2, DH_BYTES, s1, 50);
+  s2len = crypto_dh_compute_secret(dh2, p1, DH_BYTES, s2, 50);
+  test_assert(s1len > 0);
+  test_eq(s1len, s2len);
+  test_memeq(s1, s2, s1len);
+
+  {
+    /* XXXX Now fabricate some bad values and make sure they get caught,
+     * Check 0, 1, N-1, >= N, etc.
+     */
+  }
+
+ done:
+  crypto_dh_free(dh1);
+  crypto_dh_free(dh2);
+}
+
+/** Run unit tests for our random number generation function and its wrappers.
+ */
+static void
+test_crypto_rng(void)
+{
+  int i, j, allok;
+  char data1[100], data2[100];
+
+  /* Try out RNG. */
+  test_assert(! crypto_seed_rng(0));
+  crypto_rand(data1, 100);
+  crypto_rand(data2, 100);
+  test_memneq(data1,data2,100);
+  allok = 1;
+  for (i = 0; i < 100; ++i) {
+    uint64_t big;
+    char *host;
+    j = crypto_rand_int(100);
+    if (i < 0 || i >= 100)
+      allok = 0;
+    big = crypto_rand_uint64(U64_LITERAL(1)<<40);
+    if (big >= (U64_LITERAL(1)<<40))
+      allok = 0;
+    big = crypto_rand_uint64(U64_LITERAL(5));
+    if (big >= 5)
+      allok = 0;
+    host = crypto_random_hostname(3,8,"www.",".onion");
+    if (strcmpstart(host,"www.") ||
+        strcmpend(host,".onion") ||
+        strlen(host) < 13 ||
+        strlen(host) > 18)
+      allok = 0;
+    tor_free(host);
+  }
+  test_assert(allok);
+ done:
+  ;
+}
+
+/** Run unit tests for our AES functionality */
+static void
+test_crypto_aes(void)
+{
+  char *data1 = NULL, *data2 = NULL, *data3 = NULL;
+  crypto_cipher_env_t *env1 = NULL, *env2 = NULL;
+  int i, j;
+
+  data1 = tor_malloc(1024);
+  data2 = tor_malloc(1024);
+  data3 = tor_malloc(1024);
+
+  /* Now, test encryption and decryption with stream cipher. */
+  data1[0]='\0';
+  for (i = 1023; i>0; i -= 35)
+    strncat(data1, "Now is the time for all good onions", i);
+
+  memset(data2, 0, 1024);
+  memset(data3, 0, 1024);
+  env1 = crypto_new_cipher_env();
+  test_neq(env1, 0);
+  env2 = crypto_new_cipher_env();
+  test_neq(env2, 0);
+  j = crypto_cipher_generate_key(env1);
+  crypto_cipher_set_key(env2, crypto_cipher_get_key(env1));
+  crypto_cipher_encrypt_init_cipher(env1);
+  crypto_cipher_decrypt_init_cipher(env2);
+
+  /* Try encrypting 512 chars. */
+  crypto_cipher_encrypt(env1, data2, data1, 512);
+  crypto_cipher_decrypt(env2, data3, data2, 512);
+  test_memeq(data1, data3, 512);
+  test_memneq(data1, data2, 512);
+
+  /* Now encrypt 1 at a time, and get 1 at a time. */
+  for (j = 512; j < 560; ++j) {
+    crypto_cipher_encrypt(env1, data2+j, data1+j, 1);
+  }
+  for (j = 512; j < 560; ++j) {
+    crypto_cipher_decrypt(env2, data3+j, data2+j, 1);
+  }
+  test_memeq(data1, data3, 560);
+  /* Now encrypt 3 at a time, and get 5 at a time. */
+  for (j = 560; j < 1024-5; j += 3) {
+    crypto_cipher_encrypt(env1, data2+j, data1+j, 3);
+  }
+  for (j = 560; j < 1024-5; j += 5) {
+    crypto_cipher_decrypt(env2, data3+j, data2+j, 5);
+  }
+  test_memeq(data1, data3, 1024-5);
+  /* Now make sure that when we encrypt with different chunk sizes, we get
+     the same results. */
+  crypto_free_cipher_env(env2);
+  env2 = NULL;
+
+  memset(data3, 0, 1024);
+  env2 = crypto_new_cipher_env();
+  test_neq(env2, 0);
+  crypto_cipher_set_key(env2, crypto_cipher_get_key(env1));
+  crypto_cipher_encrypt_init_cipher(env2);
+  for (j = 0; j < 1024-16; j += 17) {
+    crypto_cipher_encrypt(env2, data3+j, data1+j, 17);
+  }
+  for (j= 0; j < 1024-16; ++j) {
+    if (data2[j] != data3[j]) {
+      printf("%d:  %d\t%d\n", j, (int) data2[j], (int) data3[j]);
+    }
+  }
+  test_memeq(data2, data3, 1024-16);
+  crypto_free_cipher_env(env1);
+  env1 = NULL;
+  crypto_free_cipher_env(env2);
+  env2 = NULL;
+
+  /* NIST test vector for aes. */
+  env1 = crypto_new_cipher_env(); /* IV starts at 0 */
+  crypto_cipher_set_key(env1, "\x80\x00\x00\x00\x00\x00\x00\x00"
+                              "\x00\x00\x00\x00\x00\x00\x00\x00");
+  crypto_cipher_encrypt_init_cipher(env1);
+  crypto_cipher_encrypt(env1, data1,
+                        "\x00\x00\x00\x00\x00\x00\x00\x00"
+                        "\x00\x00\x00\x00\x00\x00\x00\x00", 16);
+  test_memeq_hex(data1, "0EDD33D3C621E546455BD8BA1418BEC8");
+
+  /* Now test rollover.  All these values are originally from a python
+   * script. */
+  crypto_cipher_set_iv(env1, "\x00\x00\x00\x00\x00\x00\x00\x00"
+                             "\xff\xff\xff\xff\xff\xff\xff\xff");
+  memset(data2, 0,  1024);
+  crypto_cipher_encrypt(env1, data1, data2, 32);
+  test_memeq_hex(data1, "335fe6da56f843199066c14a00a40231"
+                        "cdd0b917dbc7186908a6bfb5ffd574d3");
+
+  crypto_cipher_set_iv(env1, "\x00\x00\x00\x00\xff\xff\xff\xff"
+                             "\xff\xff\xff\xff\xff\xff\xff\xff");
+  memset(data2, 0,  1024);
+  crypto_cipher_encrypt(env1, data1, data2, 32);
+  test_memeq_hex(data1, "e627c6423fa2d77832a02b2794094b73"
+                        "3e63c721df790d2c6469cc1953a3ffac");
+
+  crypto_cipher_set_iv(env1, "\xff\xff\xff\xff\xff\xff\xff\xff"
+                             "\xff\xff\xff\xff\xff\xff\xff\xff");
+  memset(data2, 0,  1024);
+  crypto_cipher_encrypt(env1, data1, data2, 32);
+  test_memeq_hex(data1, "2aed2bff0de54f9328efd070bf48f70a"
+                        "0EDD33D3C621E546455BD8BA1418BEC8");
+
+  /* Now check rollover on inplace cipher. */
+  crypto_cipher_set_iv(env1, "\xff\xff\xff\xff\xff\xff\xff\xff"
+                             "\xff\xff\xff\xff\xff\xff\xff\xff");
+  crypto_cipher_crypt_inplace(env1, data2, 64);
+  test_memeq_hex(data2, "2aed2bff0de54f9328efd070bf48f70a"
+                        "0EDD33D3C621E546455BD8BA1418BEC8"
+                        "93e2c5243d6839eac58503919192f7ae"
+                        "1908e67cafa08d508816659c2e693191");
+  crypto_cipher_set_iv(env1, "\xff\xff\xff\xff\xff\xff\xff\xff"
+                             "\xff\xff\xff\xff\xff\xff\xff\xff");
+  crypto_cipher_crypt_inplace(env1, data2, 64);
+  test_assert(tor_mem_is_zero(data2, 64));
+
+ done:
+  if (env1)
+    crypto_free_cipher_env(env1);
+  if (env2)
+    crypto_free_cipher_env(env2);
+  tor_free(data1);
+  tor_free(data2);
+  tor_free(data3);
+}
+
+/** Run unit tests for our SHA-1 functionality */
+static void
+test_crypto_sha(void)
+{
+  crypto_digest_env_t *d1 = NULL, *d2 = NULL;
+  int i;
+  char key[80];
+  char digest[32];
+  char data[50];
+  char d_out1[DIGEST_LEN], d_out2[DIGEST256_LEN];
+
+  /* Test SHA-1 with a test vector from the specification. */
+  i = crypto_digest(data, "abc", 3);
+  test_memeq_hex(data, "A9993E364706816ABA3E25717850C26C9CD0D89D");
+
+  /* Test SHA-256 with a test vector from the specification. */
+  i = crypto_digest256(data, "abc", 3, DIGEST_SHA256);
+  test_memeq_hex(data, "BA7816BF8F01CFEA414140DE5DAE2223B00361A3"
+                       "96177A9CB410FF61F20015AD");
+
+  /* Test HMAC-SHA-1 with test cases from RFC2202. */
+
+  /* Case 1. */
+  memset(key, 0x0b, 20);
+  crypto_hmac_sha1(digest, key, 20, "Hi There", 8);
+  test_streq(hex_str(digest, 20),
+             "B617318655057264E28BC0B6FB378C8EF146BE00");
+  /* Case 2. */
+  crypto_hmac_sha1(digest, "Jefe", 4, "what do ya want for nothing?", 28);
+  test_streq(hex_str(digest, 20),
+             "EFFCDF6AE5EB2FA2D27416D5F184DF9C259A7C79");
+
+  /* Case 4. */
+  base16_decode(key, 25,
+                "0102030405060708090a0b0c0d0e0f10111213141516171819", 50);
+  memset(data, 0xcd, 50);
+  crypto_hmac_sha1(digest, key, 25, data, 50);
+  test_streq(hex_str(digest, 20),
+             "4C9007F4026250C6BC8414F9BF50C86C2D7235DA");
+
+  /* Case 5. */
+  memset(key, 0xaa, 80);
+  crypto_hmac_sha1(digest, key, 80,
+                   "Test Using Larger Than Block-Size Key - Hash Key First",
+                   54);
+  test_streq(hex_str(digest, 20),
+             "AA4AE5E15272D00E95705637CE8A3B55ED402112");
+
+  /* Incremental digest code. */
+  d1 = crypto_new_digest_env();
+  test_assert(d1);
+  crypto_digest_add_bytes(d1, "abcdef", 6);
+  d2 = crypto_digest_dup(d1);
+  test_assert(d2);
+  crypto_digest_add_bytes(d2, "ghijkl", 6);
+  crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+  crypto_digest(d_out2, "abcdefghijkl", 12);
+  test_memeq(d_out1, d_out2, DIGEST_LEN);
+  crypto_digest_assign(d2, d1);
+  crypto_digest_add_bytes(d2, "mno", 3);
+  crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+  crypto_digest(d_out2, "abcdefmno", 9);
+  test_memeq(d_out1, d_out2, DIGEST_LEN);
+  crypto_digest_get_digest(d1, d_out1, sizeof(d_out1));
+  crypto_digest(d_out2, "abcdef", 6);
+  test_memeq(d_out1, d_out2, DIGEST_LEN);
+  crypto_free_digest_env(d1);
+  crypto_free_digest_env(d2);
+
+  /* Incremental digest code with sha256 */
+  d1 = crypto_new_digest256_env(DIGEST_SHA256);
+  test_assert(d1);
+  crypto_digest_add_bytes(d1, "abcdef", 6);
+  d2 = crypto_digest_dup(d1);
+  test_assert(d2);
+  crypto_digest_add_bytes(d2, "ghijkl", 6);
+  crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+  crypto_digest256(d_out2, "abcdefghijkl", 12, DIGEST_SHA256);
+  test_memeq(d_out1, d_out2, DIGEST_LEN);
+  crypto_digest_assign(d2, d1);
+  crypto_digest_add_bytes(d2, "mno", 3);
+  crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+  crypto_digest256(d_out2, "abcdefmno", 9, DIGEST_SHA256);
+  test_memeq(d_out1, d_out2, DIGEST_LEN);
+  crypto_digest_get_digest(d1, d_out1, sizeof(d_out1));
+  crypto_digest256(d_out2, "abcdef", 6, DIGEST_SHA256);
+  test_memeq(d_out1, d_out2, DIGEST_LEN);
+
+ done:
+  if (d1)
+    crypto_free_digest_env(d1);
+  if (d2)
+    crypto_free_digest_env(d2);
+}
+
+/** Run unit tests for our public key crypto functions */
+static void
+test_crypto_pk(void)
+{
+  crypto_pk_env_t *pk1 = NULL, *pk2 = NULL;
+  char *encoded = NULL;
+  char data1[1024], data2[1024], data3[1024];
+  size_t size;
+  int i, j, p, len;
+
+  /* Public-key ciphers */
+  pk1 = pk_generate(0);
+  pk2 = crypto_new_pk_env();
+  test_assert(pk1 && pk2);
+  test_assert(! crypto_pk_write_public_key_to_string(pk1, &encoded, &size));
+  test_assert(! crypto_pk_read_public_key_from_string(pk2, encoded, size));
+  test_eq(0, crypto_pk_cmp_keys(pk1, pk2));
+
+  test_eq(128, crypto_pk_keysize(pk1));
+  test_eq(128, crypto_pk_keysize(pk2));
+
+  test_eq(128, crypto_pk_public_encrypt(pk2, data1, "Hello whirled.", 15,
+                                        PK_PKCS1_OAEP_PADDING));
+  test_eq(128, crypto_pk_public_encrypt(pk1, data2, "Hello whirled.", 15,
+                                        PK_PKCS1_OAEP_PADDING));
+  /* oaep padding should make encryption not match */
+  test_memneq(data1, data2, 128);
+  test_eq(15, crypto_pk_private_decrypt(pk1, data3, data1, 128,
+                                        PK_PKCS1_OAEP_PADDING,1));
+  test_streq(data3, "Hello whirled.");
+  memset(data3, 0, 1024);
+  test_eq(15, crypto_pk_private_decrypt(pk1, data3, data2, 128,
+                                        PK_PKCS1_OAEP_PADDING,1));
+  test_streq(data3, "Hello whirled.");
+  /* Can't decrypt with public key. */
+  test_eq(-1, crypto_pk_private_decrypt(pk2, data3, data2, 128,
+                                        PK_PKCS1_OAEP_PADDING,1));
+  /* Try again with bad padding */
+  memcpy(data2+1, "XYZZY", 5);  /* This has fails ~ once-in-2^40 */
+  test_eq(-1, crypto_pk_private_decrypt(pk1, data3, data2, 128,
+                                        PK_PKCS1_OAEP_PADDING,1));
+
+  /* File operations: save and load private key */
+  test_assert(! crypto_pk_write_private_key_to_filename(pk1,
+                                                        get_fname("pkey1")));
+  /* failing case for read: can't read. */
+  test_assert(crypto_pk_read_private_key_from_filename(pk2,
+                                                   get_fname("xyzzy")) < 0);
+  write_str_to_file(get_fname("xyzzy"), "foobar", 6);
+  /* Failing case for read: no key. */
+  test_assert(crypto_pk_read_private_key_from_filename(pk2,
+                                                   get_fname("xyzzy")) < 0);
+  test_assert(! crypto_pk_read_private_key_from_filename(pk2,
+                                                         get_fname("pkey1")));
+  test_eq(15, crypto_pk_private_decrypt(pk2, data3, data1, 128,
+                                        PK_PKCS1_OAEP_PADDING,1));
+
+  /* Now try signing. */
+  strlcpy(data1, "Ossifrage", 1024);
+  test_eq(128, crypto_pk_private_sign(pk1, data2, data1, 10));
+  test_eq(10, crypto_pk_public_checksig(pk1, data3, data2, 128));
+  test_streq(data3, "Ossifrage");
+  /* Try signing digests. */
+  test_eq(128, crypto_pk_private_sign_digest(pk1, data2, data1, 10));
+  test_eq(20, crypto_pk_public_checksig(pk1, data3, data2, 128));
+  test_eq(0, crypto_pk_public_checksig_digest(pk1, data1, 10, data2, 128));
+  test_eq(-1, crypto_pk_public_checksig_digest(pk1, data1, 11, data2, 128));
+  /*XXXX test failed signing*/
+
+  /* Try encoding */
+  crypto_free_pk_env(pk2);
+  pk2 = NULL;
+  i = crypto_pk_asn1_encode(pk1, data1, 1024);
+  test_assert(i>0);
+  pk2 = crypto_pk_asn1_decode(data1, i);
+  test_assert(crypto_pk_cmp_keys(pk1,pk2) == 0);
+
+  /* Try with hybrid encryption wrappers. */
+  crypto_rand(data1, 1024);
+  for (i = 0; i < 3; ++i) {
+    for (j = 85; j < 140; ++j) {
+      memset(data2,0,1024);
+      memset(data3,0,1024);
+      if (i == 0 && j < 129)
+        continue;
+      p = (i==0)?PK_NO_PADDING:
+        (i==1)?PK_PKCS1_PADDING:PK_PKCS1_OAEP_PADDING;
+      len = crypto_pk_public_hybrid_encrypt(pk1,data2,data1,j,p,0);
+      test_assert(len>=0);
+      len = crypto_pk_private_hybrid_decrypt(pk1,data3,data2,len,p,1);
+      test_eq(len,j);
+      test_memeq(data1,data3,j);
+    }
+  }
+
+  /* Try copy_full */
+  crypto_free_pk_env(pk2);
+  pk2 = crypto_pk_copy_full(pk1);
+  test_assert(pk2 != NULL);
+  test_neq_ptr(pk1, pk2);
+  test_assert(crypto_pk_cmp_keys(pk1,pk2) == 0);
+
+ done:
+  if (pk1)
+    crypto_free_pk_env(pk1);
+  if (pk2)
+    crypto_free_pk_env(pk2);
+  tor_free(encoded);
+}
+
+/** Run unit tests for misc crypto formatting functionality (base64, base32,
+ * fingerprints, etc) */
+static void
+test_crypto_formats(void)
+{
+  char *data1 = NULL, *data2 = NULL, *data3 = NULL;
+  int i, j, idx;
+
+  data1 = tor_malloc(1024);
+  data2 = tor_malloc(1024);
+  data3 = tor_malloc(1024);
+  test_assert(data1 && data2 && data3);
+
+  /* Base64 tests */
+  memset(data1, 6, 1024);
+  for (idx = 0; idx < 10; ++idx) {
+    i = base64_encode(data2, 1024, data1, idx);
+    test_assert(i >= 0);
+    j = base64_decode(data3, 1024, data2, i);
+    test_eq(j,idx);
+    test_memeq(data3, data1, idx);
+  }
+
+  strlcpy(data1, "Test string that contains 35 chars.", 1024);
+  strlcat(data1, " 2nd string that contains 35 chars.", 1024);
+
+  i = base64_encode(data2, 1024, data1, 71);
+  j = base64_decode(data3, 1024, data2, i);
+  test_eq(j, 71);
+  test_streq(data3, data1);
+  test_assert(data2[i] == '\0');
+
+  crypto_rand(data1, DIGEST_LEN);
+  memset(data2, 100, 1024);
+  digest_to_base64(data2, data1);
+  test_eq(BASE64_DIGEST_LEN, strlen(data2));
+  test_eq(100, data2[BASE64_DIGEST_LEN+2]);
+  memset(data3, 99, 1024);
+  test_eq(digest_from_base64(data3, data2), 0);
+  test_memeq(data1, data3, DIGEST_LEN);
+  test_eq(99, data3[DIGEST_LEN+1]);
+
+  test_assert(digest_from_base64(data3, "###") < 0);
+
+  /* Base32 tests */
+  strlcpy(data1, "5chrs", 1024);
+  /* bit pattern is:  [35 63 68 72 73] ->
+   *        [00110101 01100011 01101000 01110010 01110011]
+   * By 5s: [00110 10101 10001 10110 10000 11100 10011 10011]
+   */
+  base32_encode(data2, 9, data1, 5);
+  test_streq(data2, "gvrwq4tt");
+
+  strlcpy(data1, "\xFF\xF5\x6D\x44\xAE\x0D\x5C\xC9\x62\xC4", 1024);
+  base32_encode(data2, 30, data1, 10);
+  test_streq(data2, "772w2rfobvomsywe");
+
+  /* Base16 tests */
+  strlcpy(data1, "6chrs\xff", 1024);
+  base16_encode(data2, 13, data1, 6);
+  test_streq(data2, "3663687273FF");
+
+  strlcpy(data1, "f0d678affc000100", 1024);
+  i = base16_decode(data2, 8, data1, 16);
+  test_eq(i,0);
+  test_memeq(data2, "\xf0\xd6\x78\xaf\xfc\x00\x01\x00",8);
+
+  /* now try some failing base16 decodes */
+  test_eq(-1, base16_decode(data2, 8, data1, 15)); /* odd input len */
+  test_eq(-1, base16_decode(data2, 7, data1, 16)); /* dest too short */
+  strlcpy(data1, "f0dz!8affc000100", 1024);
+  test_eq(-1, base16_decode(data2, 8, data1, 16));
+
+  tor_free(data1);
+  tor_free(data2);
+  tor_free(data3);
+
+  /* Add spaces to fingerprint */
+  {
+    data1 = tor_strdup("ABCD1234ABCD56780000ABCD1234ABCD56780000");
+    test_eq(strlen(data1), 40);
+    data2 = tor_malloc(FINGERPRINT_LEN+1);
+    add_spaces_to_fp(data2, FINGERPRINT_LEN+1, data1);
+    test_streq(data2, "ABCD 1234 ABCD 5678 0000 ABCD 1234 ABCD 5678 0000");
+    tor_free(data1);
+    tor_free(data2);
+  }
+
+  /* Check fingerprint */
+  {
+    test_assert(crypto_pk_check_fingerprint_syntax(
+                "ABCD 1234 ABCD 5678 0000 ABCD 1234 ABCD 5678 0000"));
+    test_assert(!crypto_pk_check_fingerprint_syntax(
+                "ABCD 1234 ABCD 5678 0000 ABCD 1234 ABCD 5678 000"));
+    test_assert(!crypto_pk_check_fingerprint_syntax(
+                "ABCD 1234 ABCD 5678 0000 ABCD 1234 ABCD 5678 00000"));
+    test_assert(!crypto_pk_check_fingerprint_syntax(
+                "ABCD 1234 ABCD 5678 0000 ABCD1234 ABCD 5678 0000"));
+    test_assert(!crypto_pk_check_fingerprint_syntax(
+                "ABCD 1234 ABCD 5678 0000 ABCD1234 ABCD 5678 00000"));
+    test_assert(!crypto_pk_check_fingerprint_syntax(
+                "ACD 1234 ABCD 5678 0000 ABCD 1234 ABCD 5678 00000"));
+  }
+
+ done:
+  tor_free(data1);
+  tor_free(data2);
+  tor_free(data3);
+}
+
+/** Run unit tests for our secret-to-key passphrase hashing functionality. */
+static void
+test_crypto_s2k(void)
+{
+  char buf[29];
+  char buf2[29];
+  char *buf3 = NULL;
+  int i;
+
+  memset(buf, 0, sizeof(buf));
+  memset(buf2, 0, sizeof(buf2));
+  buf3 = tor_malloc(65536);
+  memset(buf3, 0, 65536);
+
+  secret_to_key(buf+9, 20, "", 0, buf);
+  crypto_digest(buf2+9, buf3, 1024);
+  test_memeq(buf, buf2, 29);
+
+  memcpy(buf,"vrbacrda",8);
+  memcpy(buf2,"vrbacrda",8);
+  buf[8] = 96;
+  buf2[8] = 96;
+  secret_to_key(buf+9, 20, "12345678", 8, buf);
+  for (i = 0; i < 65536; i += 16) {
+    memcpy(buf3+i, "vrbacrda12345678", 16);
+  }
+  crypto_digest(buf2+9, buf3, 65536);
+  test_memeq(buf, buf2, 29);
+
+ done:
+  tor_free(buf3);
+}
+
+/** Test AES-CTR encryption and decryption with IV. */
+static void
+test_crypto_aes_iv(void)
+{
+  crypto_cipher_env_t *cipher;
+  char *plain, *encrypted1, *encrypted2, *decrypted1, *decrypted2;
+  char plain_1[1], plain_15[15], plain_16[16], plain_17[17];
+  char key1[16], key2[16];
+  ssize_t encrypted_size, decrypted_size;
+
+  plain = tor_malloc(4095);
+  encrypted1 = tor_malloc(4095 + 1 + 16);
+  encrypted2 = tor_malloc(4095 + 1 + 16);
+  decrypted1 = tor_malloc(4095 + 1);
+  decrypted2 = tor_malloc(4095 + 1);
+
+  crypto_rand(plain, 4095);
+  crypto_rand(key1, 16);
+  crypto_rand(key2, 16);
+  crypto_rand(plain_1, 1);
+  crypto_rand(plain_15, 15);
+  crypto_rand(plain_16, 16);
+  crypto_rand(plain_17, 17);
+  key1[0] = key2[0] + 128; /* Make sure that contents are different. */
+  /* Encrypt and decrypt with the same key. */
+  cipher = crypto_create_init_cipher(key1, 1);
+  encrypted_size = crypto_cipher_encrypt_with_iv(cipher, encrypted1, 16 + 4095,
+                                                 plain, 4095);
+  crypto_free_cipher_env(cipher);
+  cipher = NULL;
+  test_eq(encrypted_size, 16 + 4095);
+  tor_assert(encrypted_size > 0); /* This is obviously true, since 4111 is
+                                   * greater than 0, but its truth is not
+                                   * obvious to all analysis tools. */
+  cipher = crypto_create_init_cipher(key1, 0);
+  decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted1, 4095,
+                                             encrypted1, encrypted_size);
+  crypto_free_cipher_env(cipher);
+  cipher = NULL;
+  test_eq(decrypted_size, 4095);
+  tor_assert(decrypted_size > 0);
+  test_memeq(plain, decrypted1, 4095);
+  /* Encrypt a second time (with a new random initialization vector). */
+  cipher = crypto_create_init_cipher(key1, 1);
+  encrypted_size = crypto_cipher_encrypt_with_iv(cipher, encrypted2, 16 + 4095,
+                                             plain, 4095);
+  crypto_free_cipher_env(cipher);
+  cipher = NULL;
+  test_eq(encrypted_size, 16 + 4095);
+  tor_assert(encrypted_size > 0);
+  cipher = crypto_create_init_cipher(key1, 0);
+  decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted2, 4095,
+                                             encrypted2, encrypted_size);
+  crypto_free_cipher_env(cipher);
+  cipher = NULL;
+  test_eq(decrypted_size, 4095);
+  tor_assert(decrypted_size > 0);
+  test_memeq(plain, decrypted2, 4095);
+  test_memneq(encrypted1, encrypted2, encrypted_size);
+  /* Decrypt with the wrong key. */
+  cipher = crypto_create_init_cipher(key2, 0);
+  decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted2, 4095,
+                                             encrypted1, encrypted_size);
+  crypto_free_cipher_env(cipher);
+  cipher = NULL;
+  test_memneq(plain, decrypted2, encrypted_size);
+  /* Alter the initialization vector. */
+  encrypted1[0] += 42;
+  cipher = crypto_create_init_cipher(key1, 0);
+  decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted1, 4095,
+                                             encrypted1, encrypted_size);
+  crypto_free_cipher_env(cipher);
+  cipher = NULL;
+  test_memneq(plain, decrypted2, 4095);
+  /* Special length case: 1. */
+  cipher = crypto_create_init_cipher(key1, 1);
+  encrypted_size = crypto_cipher_encrypt_with_iv(cipher, encrypted1, 16 + 1,
+                                             plain_1, 1);
+  crypto_free_cipher_env(cipher);
+  cipher = NULL;
+  test_eq(encrypted_size, 16 + 1);
+  tor_assert(encrypted_size > 0);
+  cipher = crypto_create_init_cipher(key1, 0);
+  decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted1, 1,
+                                             encrypted1, encrypted_size);
+  crypto_free_cipher_env(cipher);
+  cipher = NULL;
+  test_eq(decrypted_size, 1);
+  tor_assert(decrypted_size > 0);
+  test_memeq(plain_1, decrypted1, 1);
+  /* Special length case: 15. */
+  cipher = crypto_create_init_cipher(key1, 1);
+  encrypted_size = crypto_cipher_encrypt_with_iv(cipher, encrypted1, 16 + 15,
+                                             plain_15, 15);
+  crypto_free_cipher_env(cipher);
+  cipher = NULL;
+  test_eq(encrypted_size, 16 + 15);
+  tor_assert(encrypted_size > 0);
+  cipher = crypto_create_init_cipher(key1, 0);
+  decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted1, 15,
+                                             encrypted1, encrypted_size);
+  crypto_free_cipher_env(cipher);
+  cipher = NULL;
+  test_eq(decrypted_size, 15);
+  tor_assert(decrypted_size > 0);
+  test_memeq(plain_15, decrypted1, 15);
+  /* Special length case: 16. */
+  cipher = crypto_create_init_cipher(key1, 1);
+  encrypted_size = crypto_cipher_encrypt_with_iv(cipher, encrypted1, 16 + 16,
+                                             plain_16, 16);
+  crypto_free_cipher_env(cipher);
+  cipher = NULL;
+  test_eq(encrypted_size, 16 + 16);
+  tor_assert(encrypted_size > 0);
+  cipher = crypto_create_init_cipher(key1, 0);
+  decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted1, 16,
+                                             encrypted1, encrypted_size);
+  crypto_free_cipher_env(cipher);
+  cipher = NULL;
+  test_eq(decrypted_size, 16);
+  tor_assert(decrypted_size > 0);
+  test_memeq(plain_16, decrypted1, 16);
+  /* Special length case: 17. */
+  cipher = crypto_create_init_cipher(key1, 1);
+  encrypted_size = crypto_cipher_encrypt_with_iv(cipher, encrypted1, 16 + 17,
+                                             plain_17, 17);
+  crypto_free_cipher_env(cipher);
+  cipher = NULL;
+  test_eq(encrypted_size, 16 + 17);
+  tor_assert(encrypted_size > 0);
+  cipher = crypto_create_init_cipher(key1, 0);
+  decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted1, 17,
+                                             encrypted1, encrypted_size);
+  test_eq(decrypted_size, 17);
+  tor_assert(decrypted_size > 0);
+  test_memeq(plain_17, decrypted1, 17);
+
+ done:
+  /* Free memory. */
+  tor_free(plain);
+  tor_free(encrypted1);
+  tor_free(encrypted2);
+  tor_free(decrypted1);
+  tor_free(decrypted2);
+  if (cipher)
+    crypto_free_cipher_env(cipher);
+}
+
+/** Test base32 decoding. */
+static void
+test_crypto_base32_decode(void)
+{
+  char plain[60], encoded[96 + 1], decoded[60];
+  int res;
+  crypto_rand(plain, 60);
+  /* Encode and decode a random string. */
+  base32_encode(encoded, 96 + 1, plain, 60);
+  res = base32_decode(decoded, 60, encoded, 96);
+  test_eq(res, 0);
+  test_memeq(plain, decoded, 60);
+  /* Encode, uppercase, and decode a random string. */
+  base32_encode(encoded, 96 + 1, plain, 60);
+  tor_strupper(encoded);
+  res = base32_decode(decoded, 60, encoded, 96);
+  test_eq(res, 0);
+  test_memeq(plain, decoded, 60);
+  /* Change encoded string and decode. */
+  if (encoded[0] == 'A' || encoded[0] == 'a')
+    encoded[0] = 'B';
+  else
+    encoded[0] = 'A';
+  res = base32_decode(decoded, 60, encoded, 96);
+  test_eq(res, 0);
+  test_memneq(plain, decoded, 60);
+  /* Bad encodings. */
+  encoded[0] = '!';
+  res = base32_decode(decoded, 60, encoded, 96);
+  test_assert(res < 0);
+
+ done:
+  ;
+}
+
+#define CRYPTO_LEGACY(name)                                            \
+  { #name, legacy_test_helper, 0, &legacy_setup, test_crypto_ ## name }
+
+struct testcase_t crypto_tests[] = {
+  CRYPTO_LEGACY(formats),
+  CRYPTO_LEGACY(rng),
+  CRYPTO_LEGACY(aes),
+  CRYPTO_LEGACY(sha),
+  CRYPTO_LEGACY(pk),
+  CRYPTO_LEGACY(dh),
+  CRYPTO_LEGACY(s2k),
+  CRYPTO_LEGACY(aes_iv),
+  CRYPTO_LEGACY(base32_decode),
+  END_OF_TESTCASES
+};
+


Property changes on: tor/trunk/src/test/test_crypto.c
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Added: tor/trunk/src/test/test_data.c
===================================================================
--- tor/trunk/src/test/test_data.c	                        (rev 0)
+++ tor/trunk/src/test/test_data.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,173 @@
+/* Copyright 2001-2004 Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2009, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/** First of 3 example authority certificates for unit testing. */
+const char AUTHORITY_CERT_1[] =
+"dir-key-certificate-version 3\n"
+"fingerprint D867ACF56A9D229B35C25F0090BC9867E906BE69\n"
+"dir-key-published 2008-12-12 18:07:24\n"
+"dir-key-expires 2009-12-12 18:07:24\n"
+"dir-identity-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIIBigKCAYEAveMpKlw8oD1YqFqpJchuwSR82BDhutbqgHiez3QO9FmzOctJpV+Y\n"
+"mpTYIJLS/qC+4GBKFF1VK0C4SoBrS3zri0qdXdE+vBGcyrxrjMklpxoqSKRY2011\n"
+"4eqYPghKlo5RzuqteBclGCHyNxWjUJeRKDWgvh+U/gr2uYM6fRm5q0fCzg4aECE7\n"
+"VP6fDGZrMbQI8jHpiMSoC9gkUASNEa6chLInlnP8/H5qUEW4TB9CN/q095pefuwL\n"
+"P+F+1Nz5hnM7fa5XmeMB8iM4RriUmOQlLBZgpQBMpEfWMIPcR9F1Gh3MxERqqUcH\n"
+"tmij+IZdeXg9OkCXykcabaYIhZD3meErn9Tax4oA/THduLfgli9zM0ExwzH1OooN\n"
+"L8rIcJ+2eBo3bQiQUbdYW71sl9w7nSPtircbJUa1mUvWYLPWQxFliPiQSetgJLMj\n"
+"VQqtPmV2hvN2Xk3lLfJO50qMTK7w7Gsaw8UtV4YDM1Hcjp/hQaIB1xfwhXgl+eUU\n"
+"btUa4c+cUTjHAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"dir-signing-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBALPSUInyuEu6NV3NjozplaniIEBzQXEjv1x9/+mqnwZABpYVmuy9A8nx\n"
+"eoyY3sZFsnYwNW/IZjAgG23pEmevu3F+L4myMjjaa6ORl3MgRYQ4gmuFqpefrGdm\n"
+"ywRCleh2JerkQ4VxOuq10dn/abITzLyaZzMw30KXWp5pxKXOLtxFAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"dir-key-crosscert\n"
+"-----BEGIN ID SIGNATURE-----\n"
+"FTBJNR/Hlt4T53yUMp1r/QCSMCpkHJCbYBT0R0pvYqhqFfYN5qHRSICRXaFFImIF\n"
+"0DGWmwRza6DxPKNzkm5/b7I0de9zJW1jNNdQAQK5xppAtQcAafRdu8cBonnmh9KX\n"
+"k1NrAK/X00FYywju3yl/SxCn1GddVNkHYexEudmJMPM=\n"
+"-----END ID SIGNATURE-----\n"
+"dir-key-certification\n"
+"-----BEGIN SIGNATURE-----\n"
+"pjWguLFBfELZDc6DywL6Do21SCl7LcutfpM92MEn4WYeSNcTXNR6lRX7reOEJk4e\n"
+"NwEaMt+Hl7slgeR5wjnW3OmMmRPZK9bquNWbfD+sAOV9bRFZTpXIdleAQFPlwvMF\n"
+"z/Gzwspzn4i2Yh6hySShrctMmW8YL3OM8LsBXzBhp/rG2uHlsxmIsc13DA6HWt61\n"
+"ffY72uNE6KckDGsQ4wPGP9q69y6g+X+TNio1KPbsILbePv6EjbO+rS8FiS4njPlg\n"
+"SPYry1RaUvxzxTkswIzdE1tjJrUiqpbWlTGxrH9N4OszoLm45Pc784KLULrjKIoi\n"
+"Q+vRsGrcMBAa+kDowWU6H1ryKR7KOhzRTcf2uqLE/W3ezaRwmOG+ETmoVFwbhk2X\n"
+"OlbXEM9fWP+INvFkr6Z93VYL2jGkCjV7e3xXmre/Lb92fUcYi6t5dwzfV8gJnIoG\n"
+"eCHd0K8NrQK0ipVk/7zcPDKOPeo9Y5aj/f6X/pDHtb+Dd5sT+l82G/Tqy4DIYUYR\n"
+"-----END SIGNATURE-----\n";
+
+/** The private signing key for AUTHORITY_CERT_1 */
+const char AUTHORITY_SIGNKEY_1[] =
+"-----BEGIN RSA PRIVATE KEY-----\n"
+"MIICWwIBAAKBgQCz0lCJ8rhLujVdzY6M6ZWp4iBAc0FxI79cff/pqp8GQAaWFZrs\n"
+"vQPJ8XqMmN7GRbJ2MDVvyGYwIBtt6RJnr7txfi+JsjI42mujkZdzIEWEOIJrhaqX\n"
+"n6xnZssEQpXodiXq5EOFcTrqtdHZ/2myE8y8mmczMN9Cl1qeacSlzi7cRQIDAQAB\n"
+"AoGASpzUkDinIbzU0eQt5ugxEnliOnvYRpK3nzAk1JbYPyan1PSIAPz4qn1JBTeV\n"
+"EB3xS7r7ITO8uvFHkFZqLZ2sH1uE6e4sAytJGO+kyqnlkiDTPEXpcGe99j8PH1yj\n"
+"xUOrHRlAYWjG8NEkQi+APA+HZkswE3L/viFwR2AARoE2ac0CQQDsOLdNJa+mqn6N\n"
+"1L76nEl/YgXHtKUks+beOR3IgknKEjcsJJEUHyiu0wjbXZV6gTtyQvcAePglUUD1\n"
+"R2OkOOADAkEAwuCxvHEAPeQbVt8fSvxw74vqew6LITP2Utb1dQK0E26IRPF36BsJ\n"
+"buO/gqMZv6ALq+/KxpA/pUsApbgog9uUFwJAYvHCvbrKX1pM1iXFtP1fv86UMzlU\n"
+"bxI34t8zvXftZonIuGG8rxv6E3hr3k7NvNmCx/KKuZTyA9eMCPFVKEV2dwJACn8j\n"
+"06yagLrqphE6lEVop953cM1lvRIZcHjXm8fbfzhy6pO/C6d5KJnn1NeIKYQrXMV7\n"
+"vJpEc1jI3iQ/Omr3XQJAEBIt5MlP2wlrX9om7B+32XBygUssY3cw/bXybZrtSU0/\n"
+"Yx4lqK0ca5IkTp3HevwnlWaJgbaOTUspCVshzJBhDA==\n"
+"-----END RSA PRIVATE KEY-----\n";
+
+/** Second of 3 example authority certificates for unit testing. */
+const char AUTHORITY_CERT_2[] =
+"dir-key-certificate-version 3\n"
+"fingerprint 4D44AE0470B9E88FD4558EFEC82698FB33715400\n"
+"dir-key-published 2007-06-13 16:52:32\n"
+"dir-key-expires 2008-06-13 16:52:32\n"
+"dir-identity-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIIBigKCAYEAqukDwQRm1Oy1pPY+7GNRnRNFJzEVPUBfJwC4tBH19tkvdRQPuIGI\n"
+"2jiTy/rmZ6CLcl1G0oulSgxfKEX75QdptOasZu+rKUrRRSxx0QrXhs9a7up0rpXh\n"
+"13fw3mh1Vl/As3rJYF30Hjk01BTOJMxi/HY2y0ALQytFWjiMGY74A9Y6+uDcHkB2\n"
+"KflBjxIl8zpCsXsTTnUhN5kXqaOOnK46XaUShSpXsyOxTMJXuJEtgLz9XCyA8XjW\n"
+"d75QLHucEnlTqxUAdI5YSN2KIlIJiySCVnAorDpJey2mE9VncpHQWMCv/FPFdnSU\n"
+"EMMPUc4bBShcoNFf0mMJeV2sv+dBkgKAL0GLM19PuJIThJhfN/B6+YQTxw4HEpPV\n"
+"plfUqYRN0fYC+5hCTS6rroO/uCfDR7NBtoeDNm9dQrvjfk3b/Mywah1rdWNjnVqs\n"
+"tPJaz3fc/CVBOUUexhmyktgLuwSNEYIQilQ+BydkWN/4RObhV+YSV5BgekEDVaoS\n"
+"RHw4IbYBDHVxAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"dir-signing-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBAOu3dgrQth3iqvi/UzfywaANw0bBUuMOBhnMBeiLEcRLneJHUJkVvrpR\n"
+"/EDQkdMov1e7CX6aqBKygVnbDNYjJ+bcQej8MKpuuW+zIknnz5lfnAVZO5uAmo3Y\n"
+"DpG574oQ2FFMdkWHSBloIRxSj/E4Jn1M2qJjElBXP0E33Ka/Noo7AgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"dir-key-certification\n"
+"-----BEGIN SIGNATURE-----\n"
+"Fv0Li68QUdAiChY3OklZOakHzwXAUfCzDNxkqe+HLC0n6ZECE9ZCvLVo69XmgVhH\n"
+"L5qYr2rxT6QpF+9yuOHbN9gWn8EsDcli06MlhX9TUt/IYVxHa/9tJwNoTfEw2w2D\n"
+"tyHhWm94IfOK7/Sea6jHnjckl80X+kk0ZNtAGs3/6fP4iltKNGXnvBwfgLpEgW7X\n"
+"NpDl0OLeDuA79zem2GogwQZQdoDbePByU0TJVx9jYi2Bzx2Nb2H0hRTPP6+dY0HQ\n"
+"MHb7yyyTQRad5iAUnExKhhyt22p7X3a6lgkAhq4YrNn/zVPkpnT2dzjsOydTHOW8\n"
+"2BQs33QlGNe095i47pJBDYsUgmJaXfqB/RG6dFg7jwIsc3/7dZcvcqfxY7wKcD/T\n"
+"wtogCIKxDvWbZn7f0hqYkT6uQC8Zom8bcnedmyzufOZCyA2SqQ2wvio6lznR4RIB\n"
+"a8qDHR0tPS9/VkqTPcvUWCZeY3UiDeWPjoK1nea1pz6DHDWglKPx86a0amjjayZQ\n"
+"-----END SIGNATURE-----\n";
+
+/** The private signing key for AUTHORITY_CERT_2 */
+const char AUTHORITY_SIGNKEY_2[] =
+"-----BEGIN RSA PRIVATE KEY-----\n"
+"MIICXgIBAAKBgQDrt3YK0LYd4qr4v1M38sGgDcNGwVLjDgYZzAXoixHES53iR1CZ\n"
+"Fb66UfxA0JHTKL9Xuwl+mqgSsoFZ2wzWIyfm3EHo/DCqbrlvsyJJ58+ZX5wFWTub\n"
+"gJqN2A6Rue+KENhRTHZFh0gZaCEcUo/xOCZ9TNqiYxJQVz9BN9ymvzaKOwIDAQAB\n"
+"AoGAJ+I9/ex8tCfTSA2PdisEKiHKBeHWNYb870Z/RW6qje1BhLUOZSixwfL3XLwt\n"
+"wG3nml+SZrKid69uhZaz4FPIf0tqCgURf6dDrF5vuzzr7VLVqkZHYSBp0vE6bu0R\n"
+"Sgc5QNxI2talgc4bsp0O0C+Zd4n3Yto0pXl/I6NHVAxlFBECQQD2mahkY+QEHWPV\n"
+"yRY3w3HhRmWBcrkY2zVyvPpqfn/sdHRPYW/yj4Xr/d1CO9VyFmEs4k324lIvu6LT\n"
+"WDdpPlcJAkEA9LOZv5aNeAm8ckvvXH7iv8KiONiSz0n9wlisxMhNYTEkOCo1g7jG\n"
+"AX5ZknRC9s4sWCPOBpMhloUvemdQ5FCEIwJBAMqCFwoSCf7jD8hRcUBr7QodoF/0\n"
+"kVJ7OeI2lMJ9jZnlbFp/3snn2Qeam2e38SnWfQi582KKKwnt4eIDMMXpntkCQQDI\n"
+"v1Lh11wl3y7nQZ6T7lCNatp08k+2mQgCWYcbRQweMRd6sD4I2xwt+372ZETPfyLo\n"
+"CC+sOyYx+v+RVpMJS3irAkEA6l98nMteZKmhOgyKSjdolP+ahpZunb+WnCdAtP97\n"
+"rjZyXmEZS3oe7TRCDD28GAGMmxSDvNfOOpyn14ishEs5AQ==\n"
+"-----END RSA PRIVATE KEY-----\n";
+
+/** Third of 3 example authority certificates for unit testing. */
+const char AUTHORITY_CERT_3[] =
+"dir-key-certificate-version 3\n"
+"fingerprint ED3719BF554DE9D7D59F5CA5A4F5AD121D020ED9\n"
+"dir-key-published 2007-06-13 16:52:40\n"
+"dir-key-expires 2008-06-13 16:52:40\n"
+"dir-identity-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIIBigKCAYEAtB+yw4BNxtZAG4cPaedkhWNmeij7IuNWmXjh58ZYEGurvGyHs1w4\n"
+"QlwNYI2UftSIeIGdWZ5fJ17h9P3xvO6eeJuOt4KPrNOxUbSGrELEx1Lje1fDAJ1X\n"
+"SvN+dvptusxtyFUr8afgTPrFIvYuazQ6q/Rw+NDagjmDx3h/A/enihpBnjwzeH8j\n"
+"Xzu7b+HKnzFnNfveTDdvSy0NSC6tCOnrfXo31XbXRXtlesnMIpbJClUcAv55eyai\n"
+"/PrVPCCUz8mk0sQnn2Xhv1YJmwOlQTGMfg0a0kWLmh+UWcHsGQ4VWxBZJcuzgFHG\n"
+"hu2/Fz6DXSpX5Q6B9HKoGmnH1oBh24l0kUW1jL8BxPY4YDU1Lt5t3qgcDn9dXYcI\n"
+"o8VvyI0ecSc26Q2PYFWX1hpN4VIBZ8uGaW3IpyTdNiRq0g3iMGRFEXcDlWuyMB9E\n"
+"EbSM7m/79V/z7SjDd75EP8Z0qDPESEVB8a8LbuSJtzFVE0KHd7RzkIEN5sorXspZ\n"
+"/THukftSmkIvAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"dir-signing-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBANrSZlUq38Boz3iuUOydYTJV57rTbq1bz805FP2QG2Z+2bwpgKIOZag/\n"
+"gN2A1ySJaIYLgZIg9irxrLkqlY/UAjC23y6V9fJXP1S3TXoqLmHleW8PsaDLuwTo\n"
+"hCWaR61Mx9WG7IXcodn2Z7RiCfZpSW4Rztbk5WtjQa5jPXSFOuBJAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"dir-key-certification\n"
+"-----BEGIN SIGNATURE-----\n"
+"UNXZy+4OQ8iat+gw+vg2ynvKj2BYbqZt+EAZAV3rmw6gux44U9TLRECRd6LsA08N\n"
+"4+Vz01TU81xqMgfrUy94ei2YvcfpO8art9/muWHTP9SmOX8S1uqDqLWA+n723C9A\n"
+"HyVXn4aINncO2081gJcIW5+Ul8WTCeZe/n3LVPTCKbTdqxvmrPUdCWlJTQUmb19M\n"
+"T+kcCjaEfgQGLC+Y2MHqYe/nxz+aBKqpjiWUDdjc35va6r/2e3c0jGi1B1xRZxN1\n"
+"xThPZ+CifjDoWBxJdDGlIfZRK1lMnOCJY9w9ibTXQ1UnvE4whFvmB55/t9/XLq4q\n"
+"3pnZz0H7funey3+ilmTxDohoAYT1GX+4a+3xYH07UmAFqlTzqKClj84XEHn+Cer7\n"
+"Nun9kJlJFuBgUpQjwCkzedFZKKLOHgB2h7trJfnqcBpAM8Rup1Bb5u/RcBx9gy1q\n"
+"pMc65FviIrc/Q5TUku6NNbCbnGll1599PvWuUzkG42lJ17V6psKHIsqGtVdHlCUc\n"
+"-----END SIGNATURE-----\n";
+
+/** The private signing key for AUTHORITY_CERT_3 */
+const char AUTHORITY_SIGNKEY_3[] =
+"-----BEGIN RSA PRIVATE KEY-----\n"
+"MIICXgIBAAKBgQDa0mZVKt/AaM94rlDsnWEyVee6026tW8/NORT9kBtmftm8KYCi\n"
+"DmWoP4DdgNckiWiGC4GSIPYq8ay5KpWP1AIwtt8ulfXyVz9Ut016Ki5h5XlvD7Gg\n"
+"y7sE6IQlmketTMfVhuyF3KHZ9me0Ygn2aUluEc7W5OVrY0GuYz10hTrgSQIDAQAB\n"
+"AoGBAIyoeG1AnQmildKeQpiGZackf0uhg2BeRwpFKg//5Q0Sd0Wza+M/2+q1v1Ei\n"
+"86ihxxV7KfPTykk6hmuUSwVkI28Z+5J9NYTr35EzPiUlqpo0iclTkFqrlbqSPULx\n"
+"9fQhvcOGv1c0m5CnYrHsM8eu3tagLg+6OE4abLOYX4Az5pkxAkEA/NwHhVaVJrXH\n"
+"lGDrRAfGtaD5Tzeeg1H9DNZi5lmFiSNR0O11sgDLkiZNP5oM8knyqo8Gq08hwxEb\n"
+"yqMXM3XtJQJBAN2KJbFhOjDIkvJyYvbmcP6P7vV2c9j+oUTKkFMF7vvfWunxMi9j\n"
+"ghbdUKgl7tU0VFpw7ufDDD0pkN6sua3gp1UCQQCvNzTK861U7p/GtMYyFQVf9JTt\n"
+"jMf9jYHBNInBvwTme6AFG5bz6tMlif77dJ9GAXHzODrR2Hq3thJA/3RjR3M1AkBg\n"
+"+6M4ncmtpYC+5lhwob0Bk90WU/6vFflfdhXsYoKWfNb95vsDR9qhS82Nbt25NClh\n"
+"VmMfzoFDHTkwYgj/F4PpAkEA+RaaSRP7BmbvFNqvlm8J/m0RVdAH4+p/Q5Z5u6Yo\n"
+"N7xC/gFi0qFPGKsDvD2CncAYmt+KNsd8S0JGDN4eieKn+Q==\n"
+"-----END RSA PRIVATE KEY-----\n";
+


Property changes on: tor/trunk/src/test/test_data.c
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Added: tor/trunk/src/test/test_dir.c
===================================================================
--- tor/trunk/src/test/test_dir.c	                        (rev 0)
+++ tor/trunk/src/test/test_dir.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,1096 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2009, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#define DIRSERV_PRIVATE
+#define DIRVOTE_PRIVATE
+#define ROUTER_PRIVATE
+#include "or.h"
+#include "test.h"
+
+static void
+test_dir_nicknames(void)
+{
+  test_assert( is_legal_nickname("a"));
+  test_assert(!is_legal_nickname(""));
+  test_assert(!is_legal_nickname("abcdefghijklmnopqrst")); /* 20 chars */
+  test_assert(!is_legal_nickname("hyphen-")); /* bad char */
+  test_assert( is_legal_nickname("abcdefghijklmnopqrs")); /* 19 chars */
+  test_assert(!is_legal_nickname("$AAAAAAAA01234AAAAAAAAAAAAAAAAAAAAAAAAAAA"));
+  /* valid */
+  test_assert( is_legal_nickname_or_hexdigest(
+                                 "$AAAAAAAA01234AAAAAAAAAAAAAAAAAAAAAAAAAAA"));
+  test_assert( is_legal_nickname_or_hexdigest(
+                         "$AAAAAAAA01234AAAAAAAAAAAAAAAAAAAAAAAAAAA=fred"));
+  test_assert( is_legal_nickname_or_hexdigest(
+                         "$AAAAAAAA01234AAAAAAAAAAAAAAAAAAAAAAAAAAA~fred"));
+  /* too short */
+  test_assert(!is_legal_nickname_or_hexdigest(
+                                 "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
+  /* illegal char */
+  test_assert(!is_legal_nickname_or_hexdigest(
+                                 "$AAAAAAzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
+  /* hex part too long */
+  test_assert(!is_legal_nickname_or_hexdigest(
+                         "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
+  test_assert(!is_legal_nickname_or_hexdigest(
+                         "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=fred"));
+  /* Bad nickname */
+  test_assert(!is_legal_nickname_or_hexdigest(
+                         "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="));
+  test_assert(!is_legal_nickname_or_hexdigest(
+                         "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~"));
+  test_assert(!is_legal_nickname_or_hexdigest(
+                       "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~hyphen-"));
+  test_assert(!is_legal_nickname_or_hexdigest(
+                       "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~"
+                       "abcdefghijklmnoppqrst"));
+  /* Bad extra char. */
+  test_assert(!is_legal_nickname_or_hexdigest(
+                         "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!"));
+  test_assert(is_legal_nickname_or_hexdigest("xyzzy"));
+  test_assert(is_legal_nickname_or_hexdigest("abcdefghijklmnopqrs"));
+  test_assert(!is_legal_nickname_or_hexdigest("abcdefghijklmnopqrst"));
+ done:
+  ;
+}
+
+/** Run unit tests for router descriptor generation logic. */
+static void
+test_dir_formats(void)
+{
+  char buf[8192], buf2[8192];
+  char platform[256];
+  char fingerprint[FINGERPRINT_LEN+1];
+  char *pk1_str = NULL, *pk2_str = NULL, *pk3_str = NULL, *cp;
+  size_t pk1_str_len, pk2_str_len, pk3_str_len;
+  routerinfo_t *r1=NULL, *r2=NULL;
+  crypto_pk_env_t *pk1 = NULL, *pk2 = NULL, *pk3 = NULL;
+  routerinfo_t *rp1 = NULL;
+  addr_policy_t *ex1, *ex2;
+  routerlist_t *dir1 = NULL, *dir2 = NULL;
+
+  pk1 = pk_generate(0);
+  pk2 = pk_generate(1);
+  pk3 = pk_generate(2);
+
+  get_platform_str(platform, sizeof(platform));
+  r1 = tor_malloc_zero(sizeof(routerinfo_t));
+  r1->address = tor_strdup("18.244.0.1");
+  r1->addr = 0xc0a80001u; /* 192.168.0.1 */
+  r1->cache_info.published_on = 0;
+  r1->or_port = 9000;
+  r1->dir_port = 9003;
+  r1->onion_pkey = crypto_pk_dup_key(pk1);
+  r1->identity_pkey = crypto_pk_dup_key(pk2);
+  r1->bandwidthrate = 1000;
+  r1->bandwidthburst = 5000;
+  r1->bandwidthcapacity = 10000;
+  r1->exit_policy = NULL;
+  r1->nickname = tor_strdup("Magri");
+  r1->platform = tor_strdup(platform);
+
+  ex1 = tor_malloc_zero(sizeof(addr_policy_t));
+  ex2 = tor_malloc_zero(sizeof(addr_policy_t));
+  ex1->policy_type = ADDR_POLICY_ACCEPT;
+  tor_addr_from_ipv4h(&ex1->addr, 0);
+  ex1->maskbits = 0;
+  ex1->prt_min = ex1->prt_max = 80;
+  ex2->policy_type = ADDR_POLICY_REJECT;
+  tor_addr_from_ipv4h(&ex2->addr, 18<<24);
+  ex2->maskbits = 8;
+  ex2->prt_min = ex2->prt_max = 24;
+  r2 = tor_malloc_zero(sizeof(routerinfo_t));
+  r2->address = tor_strdup("1.1.1.1");
+  r2->addr = 0x0a030201u; /* 10.3.2.1 */
+  r2->platform = tor_strdup(platform);
+  r2->cache_info.published_on = 5;
+  r2->or_port = 9005;
+  r2->dir_port = 0;
+  r2->onion_pkey = crypto_pk_dup_key(pk2);
+  r2->identity_pkey = crypto_pk_dup_key(pk1);
+  r2->bandwidthrate = r2->bandwidthburst = r2->bandwidthcapacity = 3000;
+  r2->exit_policy = smartlist_create();
+  smartlist_add(r2->exit_policy, ex2);
+  smartlist_add(r2->exit_policy, ex1);
+  r2->nickname = tor_strdup("Fred");
+
+  test_assert(!crypto_pk_write_public_key_to_string(pk1, &pk1_str,
+                                                    &pk1_str_len));
+  test_assert(!crypto_pk_write_public_key_to_string(pk2 , &pk2_str,
+                                                    &pk2_str_len));
+  test_assert(!crypto_pk_write_public_key_to_string(pk3 , &pk3_str,
+                                                    &pk3_str_len));
+
+  memset(buf, 0, 2048);
+  test_assert(router_dump_router_to_string(buf, 2048, r1, pk2)>0);
+
+  strlcpy(buf2, "router Magri 18.244.0.1 9000 0 9003\n"
+          "platform Tor "VERSION" on ", sizeof(buf2));
+  strlcat(buf2, get_uname(), sizeof(buf2));
+  strlcat(buf2, "\n"
+          "opt protocols Link 1 2 Circuit 1\n"
+          "published 1970-01-01 00:00:00\n"
+          "opt fingerprint ", sizeof(buf2));
+  test_assert(!crypto_pk_get_fingerprint(pk2, fingerprint, 1));
+  strlcat(buf2, fingerprint, sizeof(buf2));
+  strlcat(buf2, "\nuptime 0\n"
+  /* XXX the "0" above is hard-coded, but even if we made it reflect
+   * uptime, that still wouldn't make it right, because the two
+   * descriptors might be made on different seconds... hm. */
+         "bandwidth 1000 5000 10000\n"
+          "opt extra-info-digest 0000000000000000000000000000000000000000\n"
+          "onion-key\n", sizeof(buf2));
+  strlcat(buf2, pk1_str, sizeof(buf2));
+  strlcat(buf2, "signing-key\n", sizeof(buf2));
+  strlcat(buf2, pk2_str, sizeof(buf2));
+  strlcat(buf2, "opt hidden-service-dir\n", sizeof(buf2));
+  strlcat(buf2, "reject *:*\nrouter-signature\n", sizeof(buf2));
+  buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
+                             * twice */
+
+  test_streq(buf, buf2);
+
+  test_assert(router_dump_router_to_string(buf, 2048, r1, pk2)>0);
+  cp = buf;
+  rp1 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL);
+  test_assert(rp1);
+  test_streq(rp1->address, r1->address);
+  test_eq(rp1->or_port, r1->or_port);
+  //test_eq(rp1->dir_port, r1->dir_port);
+  test_eq(rp1->bandwidthrate, r1->bandwidthrate);
+  test_eq(rp1->bandwidthburst, r1->bandwidthburst);
+  test_eq(rp1->bandwidthcapacity, r1->bandwidthcapacity);
+  test_assert(crypto_pk_cmp_keys(rp1->onion_pkey, pk1) == 0);
+  test_assert(crypto_pk_cmp_keys(rp1->identity_pkey, pk2) == 0);
+  //test_assert(rp1->exit_policy == NULL);
+
+#if 0
+  /* XXX Once we have exit policies, test this again. XXX */
+  strlcpy(buf2, "router tor.tor.tor 9005 0 0 3000\n", sizeof(buf2));
+  strlcat(buf2, pk2_str, sizeof(buf2));
+  strlcat(buf2, "signing-key\n", sizeof(buf2));
+  strlcat(buf2, pk1_str, sizeof(buf2));
+  strlcat(buf2, "accept *:80\nreject 18.*:24\n\n", sizeof(buf2));
+  test_assert(router_dump_router_to_string(buf, 2048, &r2, pk2)>0);
+  test_streq(buf, buf2);
+
+  cp = buf;
+  rp2 = router_parse_entry_from_string(&cp,1);
+  test_assert(rp2);
+  test_streq(rp2->address, r2.address);
+  test_eq(rp2->or_port, r2.or_port);
+  test_eq(rp2->dir_port, r2.dir_port);
+  test_eq(rp2->bandwidth, r2.bandwidth);
+  test_assert(crypto_pk_cmp_keys(rp2->onion_pkey, pk2) == 0);
+  test_assert(crypto_pk_cmp_keys(rp2->identity_pkey, pk1) == 0);
+  test_eq(rp2->exit_policy->policy_type, EXIT_POLICY_ACCEPT);
+  test_streq(rp2->exit_policy->string, "accept *:80");
+  test_streq(rp2->exit_policy->address, "*");
+  test_streq(rp2->exit_policy->port, "80");
+  test_eq(rp2->exit_policy->next->policy_type, EXIT_POLICY_REJECT);
+  test_streq(rp2->exit_policy->next->string, "reject 18.*:24");
+  test_streq(rp2->exit_policy->next->address, "18.*");
+  test_streq(rp2->exit_policy->next->port, "24");
+  test_assert(rp2->exit_policy->next->next == NULL);
+
+  /* Okay, now for the directories. */
+  {
+    fingerprint_list = smartlist_create();
+    crypto_pk_get_fingerprint(pk2, buf, 1);
+    add_fingerprint_to_dir("Magri", buf, fingerprint_list);
+    crypto_pk_get_fingerprint(pk1, buf, 1);
+    add_fingerprint_to_dir("Fred", buf, fingerprint_list);
+  }
+
+  {
+  char d[DIGEST_LEN];
+  const char *m;
+  /* XXXX NM re-enable. */
+  /* Make sure routers aren't too far in the past any more. */
+  r1->cache_info.published_on = time(NULL);
+  r2->cache_info.published_on = time(NULL)-3*60*60;
+  test_assert(router_dump_router_to_string(buf, 2048, r1, pk2)>0);
+  test_eq(dirserv_add_descriptor(buf,&m,""), ROUTER_ADDED_NOTIFY_GENERATOR);
+  test_assert(router_dump_router_to_string(buf, 2048, r2, pk1)>0);
+  test_eq(dirserv_add_descriptor(buf,&m,""), ROUTER_ADDED_NOTIFY_GENERATOR);
+  get_options()->Nickname = tor_strdup("DirServer");
+  test_assert(!dirserv_dump_directory_to_string(&cp,pk3, 0));
+  crypto_pk_get_digest(pk3, d);
+  test_assert(!router_parse_directory(cp));
+  test_eq(2, smartlist_len(dir1->routers));
+  tor_free(cp);
+  }
+#endif
+  dirserv_free_fingerprint_list();
+
+ done:
+  if (r1)
+    routerinfo_free(r1);
+  if (r2)
+    routerinfo_free(r2);
+
+  tor_free(pk1_str);
+  tor_free(pk2_str);
+  tor_free(pk3_str);
+  if (pk1) crypto_free_pk_env(pk1);
+  if (pk2) crypto_free_pk_env(pk2);
+  if (pk3) crypto_free_pk_env(pk3);
+  if (rp1) routerinfo_free(rp1);
+  tor_free(dir1); /* XXXX And more !*/
+  tor_free(dir2); /* And more !*/
+}
+
+static void
+test_dir_versions(void)
+{
+  tor_version_t ver1;
+
+  /* Try out version parsing functionality */
+  test_eq(0, tor_version_parse("0.3.4pre2-cvs", &ver1));
+  test_eq(0, ver1.major);
+  test_eq(3, ver1.minor);
+  test_eq(4, ver1.micro);
+  test_eq(VER_PRE, ver1.status);
+  test_eq(2, ver1.patchlevel);
+  test_eq(0, tor_version_parse("0.3.4rc1", &ver1));
+  test_eq(0, ver1.major);
+  test_eq(3, ver1.minor);
+  test_eq(4, ver1.micro);
+  test_eq(VER_RC, ver1.status);
+  test_eq(1, ver1.patchlevel);
+  test_eq(0, tor_version_parse("1.3.4", &ver1));
+  test_eq(1, ver1.major);
+  test_eq(3, ver1.minor);
+  test_eq(4, ver1.micro);
+  test_eq(VER_RELEASE, ver1.status);
+  test_eq(0, ver1.patchlevel);
+  test_eq(0, tor_version_parse("1.3.4.999", &ver1));
+  test_eq(1, ver1.major);
+  test_eq(3, ver1.minor);
+  test_eq(4, ver1.micro);
+  test_eq(VER_RELEASE, ver1.status);
+  test_eq(999, ver1.patchlevel);
+  test_eq(0, tor_version_parse("0.1.2.4-alpha", &ver1));
+  test_eq(0, ver1.major);
+  test_eq(1, ver1.minor);
+  test_eq(2, ver1.micro);
+  test_eq(4, ver1.patchlevel);
+  test_eq(VER_RELEASE, ver1.status);
+  test_streq("alpha", ver1.status_tag);
+  test_eq(0, tor_version_parse("0.1.2.4", &ver1));
+  test_eq(0, ver1.major);
+  test_eq(1, ver1.minor);
+  test_eq(2, ver1.micro);
+  test_eq(4, ver1.patchlevel);
+  test_eq(VER_RELEASE, ver1.status);
+  test_streq("", ver1.status_tag);
+
+#define tt_versionstatus_op(vs1, op, vs2)                               \
+  tt_assert_test_type(vs1,vs2,#vs1" "#op" "#vs2,version_status_t,       \
+                      (_val1 op _val2),"%d")
+#define test_v_i_o(val, ver, lst)                                       \
+  tt_versionstatus_op(val, ==, tor_version_is_obsolete(ver, lst))
+
+  /* make sure tor_version_is_obsolete() works */
+  test_v_i_o(VS_OLD, "0.0.1", "Tor 0.0.2");
+  test_v_i_o(VS_OLD, "0.0.1", "0.0.2, Tor 0.0.3");
+  test_v_i_o(VS_OLD, "0.0.1", "0.0.2,Tor 0.0.3");
+  test_v_i_o(VS_OLD, "0.0.1","0.0.3,BetterTor 0.0.1");
+  test_v_i_o(VS_RECOMMENDED, "0.0.2", "Tor 0.0.2,Tor 0.0.3");
+  test_v_i_o(VS_NEW_IN_SERIES, "0.0.2", "Tor 0.0.2pre1,Tor 0.0.3");
+  test_v_i_o(VS_OLD, "0.0.2", "Tor 0.0.2.1,Tor 0.0.3");
+  test_v_i_o(VS_NEW, "0.1.0", "Tor 0.0.2,Tor 0.0.3");
+  test_v_i_o(VS_RECOMMENDED, "0.0.7rc2", "0.0.7,Tor 0.0.7rc2,Tor 0.0.8");
+  test_v_i_o(VS_OLD, "0.0.5.0", "0.0.5.1-cvs");
+  test_v_i_o(VS_NEW_IN_SERIES, "0.0.5.1-cvs", "0.0.5, 0.0.6");
+  /* Not on list, but newer than any in same series. */
+  test_v_i_o(VS_NEW_IN_SERIES, "0.1.0.3",
+             "Tor 0.1.0.2,Tor 0.0.9.5,Tor 0.1.1.0");
+  /* Series newer than any on list. */
+  test_v_i_o(VS_NEW, "0.1.2.3", "Tor 0.1.0.2,Tor 0.0.9.5,Tor 0.1.1.0");
+  /* Series older than any on list. */
+  test_v_i_o(VS_OLD, "0.0.1.3", "Tor 0.1.0.2,Tor 0.0.9.5,Tor 0.1.1.0");
+  /* Not on list, not newer than any on same series. */
+  test_v_i_o(VS_UNRECOMMENDED, "0.1.0.1",
+             "Tor 0.1.0.2,Tor 0.0.9.5,Tor 0.1.1.0");
+  /* On list, not newer than any on same series. */
+  test_v_i_o(VS_UNRECOMMENDED,
+             "0.1.0.1", "Tor 0.1.0.2,Tor 0.0.9.5,Tor 0.1.1.0");
+  test_eq(0, tor_version_as_new_as("Tor 0.0.5", "0.0.9pre1-cvs"));
+  test_eq(1, tor_version_as_new_as(
+          "Tor 0.0.8 on Darwin 64-121-192-100.c3-0."
+          "sfpo-ubr1.sfrn-sfpo.ca.cable.rcn.com Power Macintosh",
+          "0.0.8rc2"));
+  test_eq(0, tor_version_as_new_as(
+          "Tor 0.0.8 on Darwin 64-121-192-100.c3-0."
+          "sfpo-ubr1.sfrn-sfpo.ca.cable.rcn.com Power Macintosh", "0.0.8.2"));
+
+  /* Now try svn revisions. */
+  test_eq(1, tor_version_as_new_as("Tor 0.2.1.0-dev (r100)",
+                                   "Tor 0.2.1.0-dev (r99)"));
+  test_eq(1, tor_version_as_new_as("Tor 0.2.1.0-dev (r100) on Banana Jr",
+                                   "Tor 0.2.1.0-dev (r99) on Hal 9000"));
+  test_eq(1, tor_version_as_new_as("Tor 0.2.1.0-dev (r100)",
+                                   "Tor 0.2.1.0-dev on Colossus"));
+  test_eq(0, tor_version_as_new_as("Tor 0.2.1.0-dev (r99)",
+                                   "Tor 0.2.1.0-dev (r100)"));
+  test_eq(0, tor_version_as_new_as("Tor 0.2.1.0-dev (r99) on MCP",
+                                   "Tor 0.2.1.0-dev (r100) on AM"));
+  test_eq(0, tor_version_as_new_as("Tor 0.2.1.0-dev",
+                                   "Tor 0.2.1.0-dev (r99)"));
+  test_eq(1, tor_version_as_new_as("Tor 0.2.1.1",
+                                   "Tor 0.2.1.0-dev (r99)"));
+
+  /* Now try git revisions */
+  test_eq(0, tor_version_parse("0.5.6.7 (git-ff00ff)", &ver1));
+  test_eq(0, ver1.major);
+  test_eq(5, ver1.minor);
+  test_eq(6, ver1.micro);
+  test_eq(7, ver1.patchlevel);
+  test_eq(3, ver1.git_tag_len);
+  test_memeq(ver1.git_tag, "\xff\x00\xff", 3);
+  test_eq(-1, tor_version_parse("0.5.6.7 (git-ff00xx)", &ver1));
+  test_eq(-1, tor_version_parse("0.5.6.7 (git-ff00fff)", &ver1));
+  test_eq(0, tor_version_parse("0.5.6.7 (git ff00fff)", &ver1));
+ done:
+  ;
+}
+
+/** Run unit tests for misc directory functions. */
+static void
+test_dir_util(void)
+{
+  smartlist_t *sl = smartlist_create();
+  fp_pair_t *pair;
+
+  dir_split_resource_into_fingerprint_pairs(
+       /* Two pairs, out of order, with one duplicate. */
+       "73656372657420646174612E0000000000FFFFFF-"
+       "557365204145532d32353620696e73746561642e+"
+       "73656372657420646174612E0000000000FFFFFF-"
+       "557365204145532d32353620696e73746561642e+"
+       "48657861646563696d616c2069736e277420736f-"
+       "676f6f6420666f7220686964696e6720796f7572.z", sl);
+
+  test_eq(smartlist_len(sl), 2);
+  pair = smartlist_get(sl, 0);
+  test_memeq(pair->first,  "Hexadecimal isn't so", DIGEST_LEN);
+  test_memeq(pair->second, "good for hiding your", DIGEST_LEN);
+  pair = smartlist_get(sl, 1);
+  test_memeq(pair->first,  "secret data.\0\0\0\0\0\xff\xff\xff", DIGEST_LEN);
+  test_memeq(pair->second, "Use AES-256 instead.", DIGEST_LEN);
+
+ done:
+  SMARTLIST_FOREACH(sl, fp_pair_t *, pair, tor_free(pair));
+  smartlist_free(sl);
+}
+
+static void
+test_dir_measured_bw(void)
+{
+  measured_bw_line_t mbwl;
+  int i;
+  const char *lines_pass[] = {
+    "node_id=$557365204145532d32353620696e73746561642e bw=1024\n",
+    "node_id=$557365204145532d32353620696e73746561642e\t  bw=1024 \n",
+    " node_id=$557365204145532d32353620696e73746561642e  bw=1024\n",
+    "\tnoise\tnode_id=$557365204145532d32353620696e73746561642e  "
+                "bw=1024 junk=007\n",
+    "misc=junk node_id=$557365204145532d32353620696e73746561642e  "
+                "bw=1024 junk=007\n",
+    "end"
+  };
+  const char *lines_fail[] = {
+    /* Test possible python stupidity on input */
+    "node_id=None bw=1024\n",
+    "node_id=$None bw=1024\n",
+    "node_id=$557365204145532d32353620696e73746561642e bw=None\n",
+    "node_id=$557365204145532d32353620696e73746561642e bw=1024.0\n",
+    "node_id=$557365204145532d32353620696e73746561642e bw=.1024\n",
+    "node_id=$557365204145532d32353620696e73746561642e bw=1.024\n",
+    "node_id=$557365204145532d32353620696e73746561642e bw=1024 bw=0\n",
+    "node_id=$557365204145532d32353620696e73746561642e bw=1024 bw=None\n",
+    "node_id=$557365204145532d32353620696e73746561642e bw=-1024\n",
+    /* Test incomplete writes due to race conditions, partial copies, etc */
+    "node_i",
+    "node_i\n",
+    "node_id=",
+    "node_id=\n",
+    "node_id=$557365204145532d32353620696e73746561642e bw=",
+    "node_id=$557365204145532d32353620696e73746561642e bw=1024",
+    "node_id=$557365204145532d32353620696e73746561642e bw=\n",
+    "node_id=$557365204145532d32353620696e7374",
+    "node_id=$557365204145532d32353620696e7374\n",
+    "",
+    "\n",
+    " \n ",
+    " \n\n",
+    /* Test assorted noise */
+    " node_id= ",
+    "node_id==$557365204145532d32353620696e73746561642e bw==1024\n",
+    "node_id=$55736520414552d32353620696e73746561642e bw=1024\n",
+    "node_id=557365204145532d32353620696e73746561642e bw=1024\n",
+    "node_id= $557365204145532d32353620696e73746561642e bw=0.23\n",
+    "end"
+  };
+
+  for (i = 0; strcmp(lines_fail[i], "end"); i++) {
+    //fprintf(stderr, "Testing: %s\n", lines_fail[i]);
+    test_assert(measured_bw_line_parse(&mbwl, lines_fail[i]) == -1);
+  }
+
+  for (i = 0; strcmp(lines_pass[i], "end"); i++) {
+    //fprintf(stderr, "Testing: %s %d\n", lines_pass[i], TOR_ISSPACE('\n'));
+    test_assert(measured_bw_line_parse(&mbwl, lines_pass[i]) == 0);
+    test_assert(mbwl.bw == 1024);
+    test_assert(strcmp(mbwl.node_hex,
+                "557365204145532d32353620696e73746561642e") == 0);
+  }
+
+done:
+  return;
+}
+
+static void
+test_dir_param_voting(void)
+{
+  networkstatus_t vote1, vote2, vote3, vote4;
+  smartlist_t *votes = smartlist_create();
+  char *res = NULL;
+
+  /* dirvote_compute_params only looks at the net_params field of the votes,
+     so that's all we need to set.
+   */
+  memset(&vote1, 0, sizeof(vote1));
+  memset(&vote2, 0, sizeof(vote2));
+  memset(&vote3, 0, sizeof(vote3));
+  memset(&vote4, 0, sizeof(vote4));
+  vote1.net_params = smartlist_create();
+  vote2.net_params = smartlist_create();
+  vote3.net_params = smartlist_create();
+  vote4.net_params = smartlist_create();
+  smartlist_split_string(vote1.net_params,
+                         "ab=90 abcd=20 cw=50 x-yz=-99", NULL, 0, 0);
+  smartlist_split_string(vote2.net_params,
+                         "ab=27 cw=5 x-yz=88", NULL, 0, 0);
+  smartlist_split_string(vote3.net_params,
+                         "abcd=20 c=60 cw=500 x-yz=-9 zzzzz=101", NULL, 0, 0);
+  smartlist_split_string(vote4.net_params,
+                         "ab=900 abcd=200 c=1 cw=51 x-yz=100", NULL, 0, 0);
+  test_eq(100, networkstatus_get_param(&vote4, "x-yz", 50));
+  test_eq(222, networkstatus_get_param(&vote4, "foobar", 222));
+
+  smartlist_add(votes, &vote1);
+  smartlist_add(votes, &vote2);
+  smartlist_add(votes, &vote3);
+  smartlist_add(votes, &vote4);
+
+  res = dirvote_compute_params(votes);
+  test_streq(res,
+             "ab=90 abcd=20 c=1 cw=50 x-yz=-9 zzzzz=101");
+
+ done:
+  tor_free(res);
+  SMARTLIST_FOREACH(vote1.net_params, char *, cp, tor_free(cp));
+  SMARTLIST_FOREACH(vote2.net_params, char *, cp, tor_free(cp));
+  SMARTLIST_FOREACH(vote3.net_params, char *, cp, tor_free(cp));
+  SMARTLIST_FOREACH(vote4.net_params, char *, cp, tor_free(cp));
+  smartlist_free(vote1.net_params);
+  smartlist_free(vote2.net_params);
+  smartlist_free(vote3.net_params);
+  smartlist_free(vote4.net_params);
+
+  return;
+}
+
+extern const char AUTHORITY_CERT_1[];
+extern const char AUTHORITY_SIGNKEY_1[];
+extern const char AUTHORITY_CERT_2[];
+extern const char AUTHORITY_SIGNKEY_2[];
+extern const char AUTHORITY_CERT_3[];
+extern const char AUTHORITY_SIGNKEY_3[];
+
+/** Helper: Test that two networkstatus_voter_info_t do in fact represent the
+ * same voting authority, and that they do in fact have all the same
+ * information. */
+static void
+test_same_voter(networkstatus_voter_info_t *v1,
+                networkstatus_voter_info_t *v2)
+{
+  test_streq(v1->nickname, v2->nickname);
+  test_memeq(v1->identity_digest, v2->identity_digest, DIGEST_LEN);
+  test_streq(v1->address, v2->address);
+  test_eq(v1->addr, v2->addr);
+  test_eq(v1->dir_port, v2->dir_port);
+  test_eq(v1->or_port, v2->or_port);
+  test_streq(v1->contact, v2->contact);
+  test_memeq(v1->vote_digest, v2->vote_digest, DIGEST_LEN);
+ done:
+  ;
+}
+
+/** Helper: Make a new routerinfo containing the right information for a
+ * given vote_routerstatus_t. */
+static routerinfo_t *
+generate_ri_from_rs(const vote_routerstatus_t *vrs)
+{
+  routerinfo_t *r;
+  const routerstatus_t *rs = &vrs->status;
+  static time_t published = 0;
+
+  r = tor_malloc_zero(sizeof(routerinfo_t));
+  memcpy(r->cache_info.identity_digest, rs->identity_digest, DIGEST_LEN);
+  memcpy(r->cache_info.signed_descriptor_digest, rs->descriptor_digest,
+         DIGEST_LEN);
+  r->cache_info.do_not_cache = 1;
+  r->cache_info.routerlist_index = -1;
+  r->cache_info.signed_descriptor_body =
+    tor_strdup("123456789012345678901234567890123");
+  r->cache_info.signed_descriptor_len =
+    strlen(r->cache_info.signed_descriptor_body);
+  r->exit_policy = smartlist_create();
+  r->cache_info.published_on = ++published + time(NULL);
+  return r;
+}
+
+/** Run unit tests for generating and parsing V3 consensus networkstatus
+ * documents. */
+static void
+test_dir_v3_networkstatus(void)
+{
+  authority_cert_t *cert1=NULL, *cert2=NULL, *cert3=NULL;
+  crypto_pk_env_t *sign_skey_1=NULL, *sign_skey_2=NULL, *sign_skey_3=NULL;
+  crypto_pk_env_t *sign_skey_leg1=NULL;
+  const char *msg=NULL;
+
+  time_t now = time(NULL);
+  networkstatus_voter_info_t *voter;
+  networkstatus_t *vote=NULL, *v1=NULL, *v2=NULL, *v3=NULL, *con=NULL;
+  vote_routerstatus_t *vrs;
+  routerstatus_t *rs;
+  char *v1_text=NULL, *v2_text=NULL, *v3_text=NULL, *consensus_text=NULL, *cp;
+  smartlist_t *votes = smartlist_create();
+
+  /* For generating the two other consensuses. */
+  char *detached_text1=NULL, *detached_text2=NULL;
+  char *consensus_text2=NULL, *consensus_text3=NULL;
+  networkstatus_t *con2=NULL, *con3=NULL;
+  ns_detached_signatures_t *dsig1=NULL, *dsig2=NULL;
+
+  /* Parse certificates and keys. */
+  cert1 = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+  test_assert(cert1);
+  test_assert(cert1->is_cross_certified);
+  cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2, NULL);
+  test_assert(cert2);
+  cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL);
+  test_assert(cert3);
+  sign_skey_1 = crypto_new_pk_env();
+  sign_skey_2 = crypto_new_pk_env();
+  sign_skey_3 = crypto_new_pk_env();
+  sign_skey_leg1 = pk_generate(4);
+
+  test_assert(!crypto_pk_read_private_key_from_string(sign_skey_1,
+                                                      AUTHORITY_SIGNKEY_1));
+  test_assert(!crypto_pk_read_private_key_from_string(sign_skey_2,
+                                                      AUTHORITY_SIGNKEY_2));
+  test_assert(!crypto_pk_read_private_key_from_string(sign_skey_3,
+                                                      AUTHORITY_SIGNKEY_3));
+
+  test_assert(!crypto_pk_cmp_keys(sign_skey_1, cert1->signing_key));
+  test_assert(!crypto_pk_cmp_keys(sign_skey_2, cert2->signing_key));
+
+  /*
+   * Set up a vote; generate it; try to parse it.
+   */
+  vote = tor_malloc_zero(sizeof(networkstatus_t));
+  vote->type = NS_TYPE_VOTE;
+  vote->published = now;
+  vote->valid_after = now+1000;
+  vote->fresh_until = now+2000;
+  vote->valid_until = now+3000;
+  vote->vote_seconds = 100;
+  vote->dist_seconds = 200;
+  vote->supported_methods = smartlist_create();
+  smartlist_split_string(vote->supported_methods, "1 2 3", NULL, 0, -1);
+  vote->client_versions = tor_strdup("0.1.2.14,0.1.2.15");
+  vote->server_versions = tor_strdup("0.1.2.14,0.1.2.15,0.1.2.16");
+  vote->known_flags = smartlist_create();
+  smartlist_split_string(vote->known_flags,
+                     "Authority Exit Fast Guard Running Stable V2Dir Valid",
+                     0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+  vote->voters = smartlist_create();
+  voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
+  voter->nickname = tor_strdup("Voter1");
+  voter->address = tor_strdup("1.2.3.4");
+  voter->addr = 0x01020304;
+  voter->dir_port = 80;
+  voter->or_port = 9000;
+  voter->contact = tor_strdup("voter at example.com");
+  crypto_pk_get_digest(cert1->identity_key, voter->identity_digest);
+  smartlist_add(vote->voters, voter);
+  vote->cert = authority_cert_dup(cert1);
+  vote->net_params = smartlist_create();
+  smartlist_split_string(vote->net_params, "circuitwindow=101 foo=990",
+                         NULL, 0, 0);
+  vote->routerstatus_list = smartlist_create();
+  /* add the first routerstatus. */
+  vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+  rs = &vrs->status;
+  vrs->version = tor_strdup("0.1.2.14");
+  rs->published_on = now-1500;
+  strlcpy(rs->nickname, "router2", sizeof(rs->nickname));
+  memset(rs->identity_digest, 3, DIGEST_LEN);
+  memset(rs->descriptor_digest, 78, DIGEST_LEN);
+  rs->addr = 0x99008801;
+  rs->or_port = 443;
+  rs->dir_port = 8000;
+  /* all flags but running cleared */
+  rs->is_running = 1;
+  smartlist_add(vote->routerstatus_list, vrs);
+  test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
+
+  /* add the second routerstatus. */
+  vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+  rs = &vrs->status;
+  vrs->version = tor_strdup("0.2.0.5");
+  rs->published_on = now-1000;
+  strlcpy(rs->nickname, "router1", sizeof(rs->nickname));
+  memset(rs->identity_digest, 5, DIGEST_LEN);
+  memset(rs->descriptor_digest, 77, DIGEST_LEN);
+  rs->addr = 0x99009901;
+  rs->or_port = 443;
+  rs->dir_port = 0;
+  rs->is_exit = rs->is_stable = rs->is_fast = rs->is_running =
+    rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1;
+  smartlist_add(vote->routerstatus_list, vrs);
+  test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
+
+  /* add the third routerstatus. */
+  vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+  rs = &vrs->status;
+  vrs->version = tor_strdup("0.1.0.3");
+  rs->published_on = now-1000;
+  strlcpy(rs->nickname, "router3", sizeof(rs->nickname));
+  memset(rs->identity_digest, 33, DIGEST_LEN);
+  memset(rs->descriptor_digest, 79, DIGEST_LEN);
+  rs->addr = 0xAA009901;
+  rs->or_port = 400;
+  rs->dir_port = 9999;
+  rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
+    rs->is_running = rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1;
+  smartlist_add(vote->routerstatus_list, vrs);
+  test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
+
+  /* add a fourth routerstatus that is not running. */
+  vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+  rs = &vrs->status;
+  vrs->version = tor_strdup("0.1.6.3");
+  rs->published_on = now-1000;
+  strlcpy(rs->nickname, "router4", sizeof(rs->nickname));
+  memset(rs->identity_digest, 34, DIGEST_LEN);
+  memset(rs->descriptor_digest, 48, DIGEST_LEN);
+  rs->addr = 0xC0000203;
+  rs->or_port = 500;
+  rs->dir_port = 1999;
+  /* Running flag (and others) cleared */
+  smartlist_add(vote->routerstatus_list, vrs);
+  test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
+
+  /* dump the vote and try to parse it. */
+  v1_text = format_networkstatus_vote(sign_skey_1, vote);
+  test_assert(v1_text);
+  v1 = networkstatus_parse_vote_from_string(v1_text, NULL, NS_TYPE_VOTE);
+  test_assert(v1);
+
+  /* Make sure the parsed thing was right. */
+  test_eq(v1->type, NS_TYPE_VOTE);
+  test_eq(v1->published, vote->published);
+  test_eq(v1->valid_after, vote->valid_after);
+  test_eq(v1->fresh_until, vote->fresh_until);
+  test_eq(v1->valid_until, vote->valid_until);
+  test_eq(v1->vote_seconds, vote->vote_seconds);
+  test_eq(v1->dist_seconds, vote->dist_seconds);
+  test_streq(v1->client_versions, vote->client_versions);
+  test_streq(v1->server_versions, vote->server_versions);
+  test_assert(v1->voters && smartlist_len(v1->voters));
+  voter = smartlist_get(v1->voters, 0);
+  test_streq(voter->nickname, "Voter1");
+  test_streq(voter->address, "1.2.3.4");
+  test_eq(voter->addr, 0x01020304);
+  test_eq(voter->dir_port, 80);
+  test_eq(voter->or_port, 9000);
+  test_streq(voter->contact, "voter at example.com");
+  test_assert(v1->cert);
+  test_assert(!crypto_pk_cmp_keys(sign_skey_1, v1->cert->signing_key));
+  cp = smartlist_join_strings(v1->known_flags, ":", 0, NULL);
+  test_streq(cp, "Authority:Exit:Fast:Guard:Running:Stable:V2Dir:Valid");
+  tor_free(cp);
+  test_eq(smartlist_len(v1->routerstatus_list), 4);
+  /* Check the first routerstatus. */
+  vrs = smartlist_get(v1->routerstatus_list, 0);
+  rs = &vrs->status;
+  test_streq(vrs->version, "0.1.2.14");
+  test_eq(rs->published_on, now-1500);
+  test_streq(rs->nickname, "router2");
+  test_memeq(rs->identity_digest,
+             "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3",
+             DIGEST_LEN);
+  test_memeq(rs->descriptor_digest, "NNNNNNNNNNNNNNNNNNNN", DIGEST_LEN);
+  test_eq(rs->addr, 0x99008801);
+  test_eq(rs->or_port, 443);
+  test_eq(rs->dir_port, 8000);
+  test_eq(vrs->flags, U64_LITERAL(16)); // no flags except "running"
+  /* Check the second routerstatus. */
+  vrs = smartlist_get(v1->routerstatus_list, 1);
+  rs = &vrs->status;
+  test_streq(vrs->version, "0.2.0.5");
+  test_eq(rs->published_on, now-1000);
+  test_streq(rs->nickname, "router1");
+  test_memeq(rs->identity_digest,
+             "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5",
+             DIGEST_LEN);
+  test_memeq(rs->descriptor_digest, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN);
+  test_eq(rs->addr, 0x99009901);
+  test_eq(rs->or_port, 443);
+  test_eq(rs->dir_port, 0);
+  test_eq(vrs->flags, U64_LITERAL(254)); // all flags except "authority."
+
+  {
+    measured_bw_line_t mbw;
+    memset(mbw.node_id, 33, sizeof(mbw.node_id));
+    mbw.bw = 1024;
+    test_assert(measured_bw_line_apply(&mbw,
+                v1->routerstatus_list) == 1);
+    vrs = smartlist_get(v1->routerstatus_list, 2);
+    test_assert(vrs->status.has_measured_bw &&
+                vrs->status.measured_bw == 1024);
+  }
+
+  /* Generate second vote. It disagrees on some of the times,
+   * and doesn't list versions, and knows some crazy flags */
+  vote->published = now+1;
+  vote->fresh_until = now+3005;
+  vote->dist_seconds = 300;
+  authority_cert_free(vote->cert);
+  vote->cert = authority_cert_dup(cert2);
+  vote->net_params = smartlist_create();
+  smartlist_split_string(vote->net_params, "bar=2000000000 circuitwindow=20",
+                         NULL, 0, 0);
+  tor_free(vote->client_versions);
+  tor_free(vote->server_versions);
+  voter = smartlist_get(vote->voters, 0);
+  tor_free(voter->nickname);
+  tor_free(voter->address);
+  voter->nickname = tor_strdup("Voter2");
+  voter->address = tor_strdup("2.3.4.5");
+  voter->addr = 0x02030405;
+  crypto_pk_get_digest(cert2->identity_key, voter->identity_digest);
+  smartlist_add(vote->known_flags, tor_strdup("MadeOfCheese"));
+  smartlist_add(vote->known_flags, tor_strdup("MadeOfTin"));
+  smartlist_sort_strings(vote->known_flags);
+  vrs = smartlist_get(vote->routerstatus_list, 2);
+  smartlist_del_keeporder(vote->routerstatus_list, 2);
+  tor_free(vrs->version);
+  tor_free(vrs);
+  vrs = smartlist_get(vote->routerstatus_list, 0);
+  vrs->status.is_fast = 1;
+  /* generate and parse. */
+  v2_text = format_networkstatus_vote(sign_skey_2, vote);
+  test_assert(v2_text);
+  v2 = networkstatus_parse_vote_from_string(v2_text, NULL, NS_TYPE_VOTE);
+  test_assert(v2);
+  /* Check that flags come out right.*/
+  cp = smartlist_join_strings(v2->known_flags, ":", 0, NULL);
+  test_streq(cp, "Authority:Exit:Fast:Guard:MadeOfCheese:MadeOfTin:"
+             "Running:Stable:V2Dir:Valid");
+  tor_free(cp);
+  vrs = smartlist_get(v2->routerstatus_list, 1);
+  /* 1023 - authority(1) - madeofcheese(16) - madeoftin(32) */
+  test_eq(vrs->flags, U64_LITERAL(974));
+
+  /* Generate the third vote. */
+  vote->published = now;
+  vote->fresh_until = now+2003;
+  vote->dist_seconds = 250;
+  authority_cert_free(vote->cert);
+  vote->cert = authority_cert_dup(cert3);
+  vote->net_params = smartlist_create();
+  smartlist_split_string(vote->net_params, "circuitwindow=80 foo=660",
+                         NULL, 0, 0);
+  smartlist_add(vote->supported_methods, tor_strdup("4"));
+  vote->client_versions = tor_strdup("0.1.2.14,0.1.2.17");
+  vote->server_versions = tor_strdup("0.1.2.10,0.1.2.15,0.1.2.16");
+  voter = smartlist_get(vote->voters, 0);
+  tor_free(voter->nickname);
+  tor_free(voter->address);
+  voter->nickname = tor_strdup("Voter3");
+  voter->address = tor_strdup("3.4.5.6");
+  voter->addr = 0x03040506;
+  crypto_pk_get_digest(cert3->identity_key, voter->identity_digest);
+  /* This one has a legacy id. */
+  memset(voter->legacy_id_digest, (int)'A', DIGEST_LEN);
+  vrs = smartlist_get(vote->routerstatus_list, 0);
+  smartlist_del_keeporder(vote->routerstatus_list, 0);
+  tor_free(vrs->version);
+  tor_free(vrs);
+  vrs = smartlist_get(vote->routerstatus_list, 0);
+  memset(vrs->status.descriptor_digest, (int)'Z', DIGEST_LEN);
+  test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
+
+  v3_text = format_networkstatus_vote(sign_skey_3, vote);
+  test_assert(v3_text);
+
+  v3 = networkstatus_parse_vote_from_string(v3_text, NULL, NS_TYPE_VOTE);
+  test_assert(v3);
+
+  /* Compute a consensus as voter 3. */
+  smartlist_add(votes, v3);
+  smartlist_add(votes, v1);
+  smartlist_add(votes, v2);
+  consensus_text = networkstatus_compute_consensus(votes, 3,
+                                                   cert3->identity_key,
+                                                   sign_skey_3,
+                                                   "AAAAAAAAAAAAAAAAAAAA",
+                                                   sign_skey_leg1);
+  test_assert(consensus_text);
+  con = networkstatus_parse_vote_from_string(consensus_text, NULL,
+                                             NS_TYPE_CONSENSUS);
+  test_assert(con);
+  //log_notice(LD_GENERAL, "<<%s>>\n<<%s>>\n<<%s>>\n",
+  //           v1_text, v2_text, v3_text);
+
+  /* Check consensus contents. */
+  test_assert(con->type == NS_TYPE_CONSENSUS);
+  test_eq(con->published, 0); /* this field only appears in votes. */
+  test_eq(con->valid_after, now+1000);
+  test_eq(con->fresh_until, now+2003); /* median */
+  test_eq(con->valid_until, now+3000);
+  test_eq(con->vote_seconds, 100);
+  test_eq(con->dist_seconds, 250); /* median */
+  test_streq(con->client_versions, "0.1.2.14");
+  test_streq(con->server_versions, "0.1.2.15,0.1.2.16");
+  cp = smartlist_join_strings(v2->known_flags, ":", 0, NULL);
+  test_streq(cp, "Authority:Exit:Fast:Guard:MadeOfCheese:MadeOfTin:"
+             "Running:Stable:V2Dir:Valid");
+  tor_free(cp);
+  cp = smartlist_join_strings(con->net_params, ":", 0, NULL);
+  test_streq(cp, "bar=2000000000:circuitwindow=80:foo=660");
+  tor_free(cp);
+
+  test_eq(4, smartlist_len(con->voters)); /*3 voters, 1 legacy key.*/
+  /* The voter id digests should be in this order. */
+  test_assert(memcmp(cert2->cache_info.identity_digest,
+                     cert1->cache_info.identity_digest,DIGEST_LEN)<0);
+  test_assert(memcmp(cert1->cache_info.identity_digest,
+                     cert3->cache_info.identity_digest,DIGEST_LEN)<0);
+  test_same_voter(smartlist_get(con->voters, 1),
+                  smartlist_get(v2->voters, 0));
+  test_same_voter(smartlist_get(con->voters, 2),
+                  smartlist_get(v1->voters, 0));
+  test_same_voter(smartlist_get(con->voters, 3),
+                  smartlist_get(v3->voters, 0));
+
+  test_assert(!con->cert);
+  test_eq(2, smartlist_len(con->routerstatus_list));
+  /* There should be two listed routers: one with identity 3, one with
+   * identity 5. */
+  /* This one showed up in 2 digests. */
+  rs = smartlist_get(con->routerstatus_list, 0);
+  test_memeq(rs->identity_digest,
+             "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3",
+             DIGEST_LEN);
+  test_memeq(rs->descriptor_digest, "NNNNNNNNNNNNNNNNNNNN", DIGEST_LEN);
+  test_assert(!rs->is_authority);
+  test_assert(!rs->is_exit);
+  test_assert(!rs->is_fast);
+  test_assert(!rs->is_possible_guard);
+  test_assert(!rs->is_stable);
+  test_assert(rs->is_running); /* If it wasn't running it wouldn't be here */
+  test_assert(!rs->is_v2_dir);
+  test_assert(!rs->is_valid);
+  test_assert(!rs->is_named);
+  /* XXXX check version */
+
+  rs = smartlist_get(con->routerstatus_list, 1);
+  /* This one showed up in 3 digests. Twice with ID 'M', once with 'Z'.  */
+  test_memeq(rs->identity_digest,
+             "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5",
+             DIGEST_LEN);
+  test_streq(rs->nickname, "router1");
+  test_memeq(rs->descriptor_digest, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN);
+  test_eq(rs->published_on, now-1000);
+  test_eq(rs->addr, 0x99009901);
+  test_eq(rs->or_port, 443);
+  test_eq(rs->dir_port, 0);
+  test_assert(!rs->is_authority);
+  test_assert(rs->is_exit);
+  test_assert(rs->is_fast);
+  test_assert(rs->is_possible_guard);
+  test_assert(rs->is_stable);
+  test_assert(rs->is_running);
+  test_assert(rs->is_v2_dir);
+  test_assert(rs->is_valid);
+  test_assert(!rs->is_named);
+  /* XXXX check version */
+  // x231
+  // x213
+
+  /* Check signatures.  the first voter is a pseudo-entry with a legacy key.
+   * The second one hasn't signed.  The fourth one has signed: validate it. */
+  voter = smartlist_get(con->voters, 1);
+  test_assert(!voter->signature);
+  test_assert(!voter->good_signature);
+  test_assert(!voter->bad_signature);
+
+  voter = smartlist_get(con->voters, 3);
+  test_assert(voter->signature);
+  test_assert(!voter->good_signature);
+  test_assert(!voter->bad_signature);
+  test_assert(!networkstatus_check_voter_signature(con,
+                                               smartlist_get(con->voters, 3),
+                                               cert3));
+  test_assert(voter->signature);
+  test_assert(voter->good_signature);
+  test_assert(!voter->bad_signature);
+
+  {
+    const char *msg=NULL;
+    /* Compute the other two signed consensuses. */
+    smartlist_shuffle(votes);
+    consensus_text2 = networkstatus_compute_consensus(votes, 3,
+                                                      cert2->identity_key,
+                                                      sign_skey_2, NULL,NULL);
+    smartlist_shuffle(votes);
+    consensus_text3 = networkstatus_compute_consensus(votes, 3,
+                                                      cert1->identity_key,
+                                                      sign_skey_1, NULL,NULL);
+    test_assert(consensus_text2);
+    test_assert(consensus_text3);
+    con2 = networkstatus_parse_vote_from_string(consensus_text2, NULL,
+                                                NS_TYPE_CONSENSUS);
+    con3 = networkstatus_parse_vote_from_string(consensus_text3, NULL,
+                                                NS_TYPE_CONSENSUS);
+    test_assert(con2);
+    test_assert(con3);
+
+    /* All three should have the same digest. */
+    test_memeq(con->networkstatus_digest, con2->networkstatus_digest,
+               DIGEST_LEN);
+    test_memeq(con->networkstatus_digest, con3->networkstatus_digest,
+               DIGEST_LEN);
+
+    /* Extract a detached signature from con3. */
+    detached_text1 = networkstatus_get_detached_signatures(con3);
+    tor_assert(detached_text1);
+    /* Try to parse it. */
+    dsig1 = networkstatus_parse_detached_signatures(detached_text1, NULL);
+    tor_assert(dsig1);
+
+    /* Are parsed values as expected? */
+    test_eq(dsig1->valid_after, con3->valid_after);
+    test_eq(dsig1->fresh_until, con3->fresh_until);
+    test_eq(dsig1->valid_until, con3->valid_until);
+    test_memeq(dsig1->networkstatus_digest, con3->networkstatus_digest,
+               DIGEST_LEN);
+    test_eq(1, smartlist_len(dsig1->signatures));
+    voter = smartlist_get(dsig1->signatures, 0);
+    test_memeq(voter->identity_digest, cert1->cache_info.identity_digest,
+               DIGEST_LEN);
+
+    /* Try adding it to con2. */
+    detached_text2 = networkstatus_get_detached_signatures(con2);
+    test_eq(1, networkstatus_add_detached_signatures(con2, dsig1, &msg));
+    tor_free(detached_text2);
+    detached_text2 = networkstatus_get_detached_signatures(con2);
+    //printf("\n<%s>\n", detached_text2);
+    dsig2 = networkstatus_parse_detached_signatures(detached_text2, NULL);
+    test_assert(dsig2);
+    /*
+    printf("\n");
+    SMARTLIST_FOREACH(dsig2->signatures, networkstatus_voter_info_t *, vi, {
+        char hd[64];
+        base16_encode(hd, sizeof(hd), vi->identity_digest, DIGEST_LEN);
+        printf("%s\n", hd);
+      });
+    */
+    test_eq(2, smartlist_len(dsig2->signatures));
+
+    /* Try adding to con2 twice; verify that nothing changes. */
+    test_eq(0, networkstatus_add_detached_signatures(con2, dsig1, &msg));
+
+    /* Add to con. */
+    test_eq(2, networkstatus_add_detached_signatures(con, dsig2, &msg));
+    /* Check signatures */
+    test_assert(!networkstatus_check_voter_signature(con,
+                                               smartlist_get(con->voters, 1),
+                                               cert2));
+    test_assert(!networkstatus_check_voter_signature(con,
+                                               smartlist_get(con->voters, 2),
+                                               cert1));
+
+  }
+
+ done:
+  smartlist_free(votes);
+  tor_free(v1_text);
+  tor_free(v2_text);
+  tor_free(v3_text);
+  tor_free(consensus_text);
+
+  if (vote)
+    networkstatus_vote_free(vote);
+  if (v1)
+    networkstatus_vote_free(v1);
+  if (v2)
+    networkstatus_vote_free(v2);
+  if (v3)
+    networkstatus_vote_free(v3);
+  if (con)
+    networkstatus_vote_free(con);
+  if (sign_skey_1)
+    crypto_free_pk_env(sign_skey_1);
+  if (sign_skey_2)
+    crypto_free_pk_env(sign_skey_2);
+  if (sign_skey_3)
+    crypto_free_pk_env(sign_skey_3);
+  if (sign_skey_leg1)
+    crypto_free_pk_env(sign_skey_leg1);
+  if (cert1)
+    authority_cert_free(cert1);
+  if (cert2)
+    authority_cert_free(cert2);
+  if (cert3)
+    authority_cert_free(cert3);
+
+  tor_free(consensus_text2);
+  tor_free(consensus_text3);
+  tor_free(detached_text1);
+  tor_free(detached_text2);
+  if (con2)
+    networkstatus_vote_free(con2);
+  if (con3)
+    networkstatus_vote_free(con3);
+  if (dsig1)
+    ns_detached_signatures_free(dsig1);
+  if (dsig2)
+    ns_detached_signatures_free(dsig2);
+}
+
+#define DIR_LEGACY(name)                                                   \
+  { #name, legacy_test_helper, 0, &legacy_setup, test_dir_ ## name }
+
+struct testcase_t dir_tests[] = {
+  DIR_LEGACY(nicknames),
+  DIR_LEGACY(formats),
+  DIR_LEGACY(versions),
+  DIR_LEGACY(util),
+  DIR_LEGACY(measured_bw),
+  DIR_LEGACY(param_voting),
+  DIR_LEGACY(v3_networkstatus),
+  END_OF_TESTCASES
+};
+


Property changes on: tor/trunk/src/test/test_dir.c
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Added: tor/trunk/src/test/test_util.c
===================================================================
--- tor/trunk/src/test/test_util.c	                        (rev 0)
+++ tor/trunk/src/test/test_util.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,1044 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2009, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#define CONTROL_PRIVATE
+#define MEMPOOL_PRIVATE
+#include "or.h"
+#include "test.h"
+#include "mempool.h"
+#include "memarea.h"
+
+static void
+test_util_time(void)
+{
+  struct timeval start, end;
+  struct tm a_time;
+  char timestr[RFC1123_TIME_LEN+1];
+  time_t t_res;
+  int i;
+
+  start.tv_sec = 5;
+  start.tv_usec = 5000;
+
+  end.tv_sec = 5;
+  end.tv_usec = 5000;
+
+  test_eq(0L, tv_udiff(&start, &end));
+
+  end.tv_usec = 7000;
+
+  test_eq(2000L, tv_udiff(&start, &end));
+
+  end.tv_sec = 6;
+
+  test_eq(1002000L, tv_udiff(&start, &end));
+
+  end.tv_usec = 0;
+
+  test_eq(995000L, tv_udiff(&start, &end));
+
+  end.tv_sec = 4;
+
+  test_eq(-1005000L, tv_udiff(&start, &end));
+
+  end.tv_usec = 999990;
+  start.tv_sec = 1;
+  start.tv_usec = 500;
+
+  /* The test values here are confirmed to be correct on a platform
+   * with a working timegm. */
+  a_time.tm_year = 2003-1900;
+  a_time.tm_mon = 7;
+  a_time.tm_mday = 30;
+  a_time.tm_hour = 6;
+  a_time.tm_min = 14;
+  a_time.tm_sec = 55;
+  test_eq((time_t) 1062224095UL, tor_timegm(&a_time));
+  a_time.tm_year = 2004-1900; /* Try a leap year, after feb. */
+  test_eq((time_t) 1093846495UL, tor_timegm(&a_time));
+  a_time.tm_mon = 1;          /* Try a leap year, in feb. */
+  a_time.tm_mday = 10;
+  test_eq((time_t) 1076393695UL, tor_timegm(&a_time));
+
+  format_rfc1123_time(timestr, 0);
+  test_streq("Thu, 01 Jan 1970 00:00:00 GMT", timestr);
+  format_rfc1123_time(timestr, (time_t)1091580502UL);
+  test_streq("Wed, 04 Aug 2004 00:48:22 GMT", timestr);
+
+  t_res = 0;
+  i = parse_rfc1123_time(timestr, &t_res);
+  test_eq(i,0);
+  test_eq(t_res, (time_t)1091580502UL);
+  test_eq(-1, parse_rfc1123_time("Wed, zz Aug 2004 99-99x99 GMT", &t_res));
+
+  tor_gettimeofday(&start);
+  /* now make sure time works. */
+  tor_gettimeofday(&end);
+  /* We might've timewarped a little. */
+  tt_int_op(tv_udiff(&start, &end), >=, -5000);
+
+ done:
+  ;
+}
+
+static void
+test_util_config_line(void)
+{
+  char buf[1024];
+  char *k, *v;
+  const char *str;
+
+  /* Test parse_config_line_from_str */
+  strlcpy(buf, "k v\n" " key    value with spaces   \n" "keykey val\n"
+          "k2\n"
+          "k3 \n" "\n" "   \n" "#comment\n"
+          "k4#a\n" "k5#abc\n" "k6 val #with comment\n"
+          "kseven   \"a quoted 'string\"\n"
+          "k8 \"a \\x71uoted\\n\\\"str\\\\ing\\t\\001\\01\\1\\\"\"\n"
+          , sizeof(buf));
+  str = buf;
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k");
+  test_streq(v, "v");
+  tor_free(k); tor_free(v);
+  test_assert(!strcmpstart(str, "key    value with"));
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "key");
+  test_streq(v, "value with spaces");
+  tor_free(k); tor_free(v);
+  test_assert(!strcmpstart(str, "keykey"));
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "keykey");
+  test_streq(v, "val");
+  tor_free(k); tor_free(v);
+  test_assert(!strcmpstart(str, "k2\n"));
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k2");
+  test_streq(v, "");
+  tor_free(k); tor_free(v);
+  test_assert(!strcmpstart(str, "k3 \n"));
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k3");
+  test_streq(v, "");
+  tor_free(k); tor_free(v);
+  test_assert(!strcmpstart(str, "#comment"));
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k4");
+  test_streq(v, "");
+  tor_free(k); tor_free(v);
+  test_assert(!strcmpstart(str, "k5#abc"));
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k5");
+  test_streq(v, "");
+  tor_free(k); tor_free(v);
+  test_assert(!strcmpstart(str, "k6"));
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k6");
+  test_streq(v, "val");
+  tor_free(k); tor_free(v);
+  test_assert(!strcmpstart(str, "kseven"));
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "kseven");
+  test_streq(v, "a quoted \'string");
+  tor_free(k); tor_free(v);
+  test_assert(!strcmpstart(str, "k8 "));
+
+  str = parse_config_line_from_str(str, &k, &v);
+  test_streq(k, "k8");
+  test_streq(v, "a quoted\n\"str\\ing\t\x01\x01\x01\"");
+  tor_free(k); tor_free(v);
+  test_streq(str, "");
+ done:
+  ;
+}
+
+/** Test basic string functionality. */
+static void
+test_util_strmisc(void)
+{
+  char buf[1024];
+  int i;
+  char *cp;
+
+  /* Tests for corner cases of strl operations */
+  test_eq(5, strlcpy(buf, "Hello", 0));
+  strlcpy(buf, "Hello", sizeof(buf));
+  test_eq(10, strlcat(buf, "Hello", 5));
+
+  /* Test tor_strstrip() */
+  strlcpy(buf, "Testing 1 2 3", sizeof(buf));
+  tor_strstrip(buf, ",!");
+  test_streq(buf, "Testing 1 2 3");
+  strlcpy(buf, "!Testing 1 2 3?", sizeof(buf));
+  tor_strstrip(buf, "!? ");
+  test_streq(buf, "Testing123");
+
+  /* Test tor_parse_long. */
+  test_eq(10L, tor_parse_long("10",10,0,100,NULL,NULL));
+  test_eq(0L, tor_parse_long("10",10,50,100,NULL,NULL));
+  test_eq(-50L, tor_parse_long("-50",10,-100,100,NULL,NULL));
+
+  /* Test tor_parse_ulong */
+  test_eq(10UL, tor_parse_ulong("10",10,0,100,NULL,NULL));
+  test_eq(0UL, tor_parse_ulong("10",10,50,100,NULL,NULL));
+
+  /* Test tor_parse_uint64. */
+  test_assert(U64_LITERAL(10) == tor_parse_uint64("10 x",10,0,100, &i, &cp));
+  test_assert(i == 1);
+  test_streq(cp, " x");
+  test_assert(U64_LITERAL(12345678901) ==
+              tor_parse_uint64("12345678901",10,0,UINT64_MAX, &i, &cp));
+  test_assert(i == 1);
+  test_streq(cp, "");
+  test_assert(U64_LITERAL(0) ==
+              tor_parse_uint64("12345678901",10,500,INT32_MAX, &i, &cp));
+  test_assert(i == 0);
+
+  {
+  /* Test tor_parse_double. */
+  double d = tor_parse_double("10", 0, UINT64_MAX,&i,NULL);
+  test_assert(i == 1);
+  test_assert(DBL_TO_U64(d) == 10);
+  d = tor_parse_double("0", 0, UINT64_MAX,&i,NULL);
+  test_assert(i == 1);
+  test_assert(DBL_TO_U64(d) == 0);
+  d = tor_parse_double(" ", 0, UINT64_MAX,&i,NULL);
+  test_assert(i == 0);
+  d = tor_parse_double(".0a", 0, UINT64_MAX,&i,NULL);
+  test_assert(i == 0);
+  d = tor_parse_double(".0a", 0, UINT64_MAX,&i,&cp);
+  test_assert(i == 1);
+  d = tor_parse_double("-.0", 0, UINT64_MAX,&i,NULL);
+  test_assert(i == 1);
+  }
+
+  /* Test failing snprintf cases */
+  test_eq(-1, tor_snprintf(buf, 0, "Foo"));
+  test_eq(-1, tor_snprintf(buf, 2, "Foo"));
+
+  /* Test printf with uint64 */
+  tor_snprintf(buf, sizeof(buf), "x!"U64_FORMAT"!x",
+               U64_PRINTF_ARG(U64_LITERAL(12345678901)));
+  test_streq(buf, "x!12345678901!x");
+
+  /* Test for strcmpstart and strcmpend. */
+  test_assert(strcmpstart("abcdef", "abcdef")==0);
+  test_assert(strcmpstart("abcdef", "abc")==0);
+  test_assert(strcmpstart("abcdef", "abd")<0);
+  test_assert(strcmpstart("abcdef", "abb")>0);
+  test_assert(strcmpstart("ab", "abb")<0);
+
+  test_assert(strcmpend("abcdef", "abcdef")==0);
+  test_assert(strcmpend("abcdef", "def")==0);
+  test_assert(strcmpend("abcdef", "deg")<0);
+  test_assert(strcmpend("abcdef", "dee")>0);
+  test_assert(strcmpend("ab", "abb")<0);
+
+  test_assert(strcasecmpend("AbcDEF", "abcdef")==0);
+  test_assert(strcasecmpend("abcdef", "dEF")==0);
+  test_assert(strcasecmpend("abcDEf", "deg")<0);
+  test_assert(strcasecmpend("abcdef", "DEE")>0);
+  test_assert(strcasecmpend("ab", "abB")<0);
+
+  /* Test mem_is_zero */
+  memset(buf,0,128);
+  buf[128] = 'x';
+  test_assert(tor_digest_is_zero(buf));
+  test_assert(tor_mem_is_zero(buf, 10));
+  test_assert(tor_mem_is_zero(buf, 20));
+  test_assert(tor_mem_is_zero(buf, 128));
+  test_assert(!tor_mem_is_zero(buf, 129));
+  buf[60] = (char)255;
+  test_assert(!tor_mem_is_zero(buf, 128));
+  buf[0] = (char)1;
+  test_assert(!tor_mem_is_zero(buf, 10));
+
+  /* Test 'escaped' */
+  test_streq("\"\"", escaped(""));
+  test_streq("\"abcd\"", escaped("abcd"));
+  test_streq("\"\\\\\\n\\r\\t\\\"\\'\"", escaped("\\\n\r\t\"\'"));
+  test_streq("\"z\\001abc\\277d\"", escaped("z\001abc\277d"));
+  test_assert(NULL == escaped(NULL));
+
+  /* Test strndup and memdup */
+  {
+    const char *s = "abcdefghijklmnopqrstuvwxyz";
+    cp = tor_strndup(s, 30);
+    test_streq(cp, s); /* same string, */
+    test_neq(cp, s); /* but different pointers. */
+    tor_free(cp);
+
+    cp = tor_strndup(s, 5);
+    test_streq(cp, "abcde");
+    tor_free(cp);
+
+    s = "a\0b\0c\0d\0e\0";
+    cp = tor_memdup(s,10);
+    test_memeq(cp, s, 10); /* same ram, */
+    test_neq(cp, s); /* but different pointers. */
+    tor_free(cp);
+  }
+
+  /* Test str-foo functions */
+  cp = tor_strdup("abcdef");
+  test_assert(tor_strisnonupper(cp));
+  cp[3] = 'D';
+  test_assert(!tor_strisnonupper(cp));
+  tor_strupper(cp);
+  test_streq(cp, "ABCDEF");
+  test_assert(tor_strisprint(cp));
+  cp[3] = 3;
+  test_assert(!tor_strisprint(cp));
+  tor_free(cp);
+
+  /* Test eat_whitespace. */
+  {
+    const char *s = "  \n a";
+    test_eq_ptr(eat_whitespace(s), s+4);
+    s = "abcd";
+    test_eq_ptr(eat_whitespace(s), s);
+    s = "#xyz\nab";
+    test_eq_ptr(eat_whitespace(s), s+5);
+  }
+
+  /* Test memmem and memstr */
+  {
+    const char *haystack = "abcde";
+    tor_assert(!tor_memmem(haystack, 5, "ef", 2));
+    test_eq_ptr(tor_memmem(haystack, 5, "cd", 2), haystack + 2);
+    test_eq_ptr(tor_memmem(haystack, 5, "cde", 3), haystack + 2);
+    haystack = "ababcad";
+    test_eq_ptr(tor_memmem(haystack, 7, "abc", 3), haystack + 2);
+    test_eq_ptr(tor_memstr(haystack, 7, "abc"), haystack + 2);
+    test_assert(!tor_memstr(haystack, 7, "fe"));
+    test_assert(!tor_memstr(haystack, 7, "longerthantheoriginal"));
+  }
+
+  /* Test wrap_string */
+  {
+    smartlist_t *sl = smartlist_create();
+    wrap_string(sl, "This is a test of string wrapping functionality: woot.",
+                10, "", "");
+    cp = smartlist_join_strings(sl, "", 0, NULL);
+    test_streq(cp,
+            "This is a\ntest of\nstring\nwrapping\nfunctional\nity: woot.\n");
+    tor_free(cp);
+    SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+    smartlist_clear(sl);
+
+    wrap_string(sl, "This is a test of string wrapping functionality: woot.",
+                16, "### ", "# ");
+    cp = smartlist_join_strings(sl, "", 0, NULL);
+    test_streq(cp,
+             "### This is a\n# test of string\n# wrapping\n# functionality:\n"
+             "# woot.\n");
+
+    tor_free(cp);
+    SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+    smartlist_free(sl);
+  }
+ done:
+  ;
+}
+
+static void
+test_util_pow2(void)
+{
+  /* Test tor_log2(). */
+  test_eq(tor_log2(64), 6);
+  test_eq(tor_log2(65), 6);
+  test_eq(tor_log2(63), 5);
+  test_eq(tor_log2(1), 0);
+  test_eq(tor_log2(2), 1);
+  test_eq(tor_log2(3), 1);
+  test_eq(tor_log2(4), 2);
+  test_eq(tor_log2(5), 2);
+  test_eq(tor_log2(U64_LITERAL(40000000000000000)), 55);
+  test_eq(tor_log2(UINT64_MAX), 63);
+
+  /* Test round_to_power_of_2 */
+  test_eq(round_to_power_of_2(120), 128);
+  test_eq(round_to_power_of_2(128), 128);
+  test_eq(round_to_power_of_2(130), 128);
+  test_eq(round_to_power_of_2(U64_LITERAL(40000000000000000)),
+          U64_LITERAL(1)<<55);
+  test_eq(round_to_power_of_2(0), 2);
+
+ done:
+  ;
+}
+
+/** mutex for thread test to stop the threads hitting data at the same time. */
+static tor_mutex_t *_thread_test_mutex = NULL;
+/** mutexes for the thread test to make sure that the threads have to
+ * interleave somewhat. */
+static tor_mutex_t *_thread_test_start1 = NULL,
+                   *_thread_test_start2 = NULL;
+/** Shared strmap for the thread test. */
+static strmap_t *_thread_test_strmap = NULL;
+/** The name of thread1 for the thread test */
+static char *_thread1_name = NULL;
+/** The name of thread2 for the thread test */
+static char *_thread2_name = NULL;
+
+static void _thread_test_func(void* _s) ATTR_NORETURN;
+
+/** How many iterations have the threads in the unit test run? */
+static int t1_count = 0, t2_count = 0;
+
+/** Helper function for threading unit tests: This function runs in a
+ * subthread. It grabs its own mutex (start1 or start2) to make sure that it
+ * should start, then it repeatedly alters _test_thread_strmap protected by
+ * _thread_test_mutex. */
+static void
+_thread_test_func(void* _s)
+{
+  char *s = _s;
+  int i, *count;
+  tor_mutex_t *m;
+  char buf[64];
+  char **cp;
+  if (!strcmp(s, "thread 1")) {
+    m = _thread_test_start1;
+    cp = &_thread1_name;
+    count = &t1_count;
+  } else {
+    m = _thread_test_start2;
+    cp = &_thread2_name;
+    count = &t2_count;
+  }
+  tor_mutex_acquire(m);
+
+  tor_snprintf(buf, sizeof(buf), "%lu", tor_get_thread_id());
+  *cp = tor_strdup(buf);
+
+  for (i=0; i<10000; ++i) {
+    tor_mutex_acquire(_thread_test_mutex);
+    strmap_set(_thread_test_strmap, "last to run", *cp);
+    ++*count;
+    tor_mutex_release(_thread_test_mutex);
+  }
+  tor_mutex_acquire(_thread_test_mutex);
+  strmap_set(_thread_test_strmap, s, *cp);
+  tor_mutex_release(_thread_test_mutex);
+
+  tor_mutex_release(m);
+
+  spawn_exit();
+}
+
+/** Run unit tests for threading logic. */
+static void
+test_util_threads(void)
+{
+  char *s1 = NULL, *s2 = NULL;
+  int done = 0, timedout = 0;
+  time_t started;
+#ifndef TOR_IS_MULTITHREADED
+  /* Skip this test if we aren't threading. We should be threading most
+   * everywhere by now. */
+  if (1)
+    return;
+#endif
+  _thread_test_mutex = tor_mutex_new();
+  _thread_test_start1 = tor_mutex_new();
+  _thread_test_start2 = tor_mutex_new();
+  _thread_test_strmap = strmap_new();
+  s1 = tor_strdup("thread 1");
+  s2 = tor_strdup("thread 2");
+  tor_mutex_acquire(_thread_test_start1);
+  tor_mutex_acquire(_thread_test_start2);
+  spawn_func(_thread_test_func, s1);
+  spawn_func(_thread_test_func, s2);
+  tor_mutex_release(_thread_test_start2);
+  tor_mutex_release(_thread_test_start1);
+  started = time(NULL);
+  while (!done) {
+    tor_mutex_acquire(_thread_test_mutex);
+    strmap_assert_ok(_thread_test_strmap);
+    if (strmap_get(_thread_test_strmap, "thread 1") &&
+        strmap_get(_thread_test_strmap, "thread 2")) {
+      done = 1;
+    } else if (time(NULL) > started + 25) {
+      timedout = done = 1;
+    }
+    tor_mutex_release(_thread_test_mutex);
+  }
+  tor_mutex_free(_thread_test_mutex);
+
+  tor_mutex_acquire(_thread_test_start1);
+  tor_mutex_release(_thread_test_start1);
+  tor_mutex_acquire(_thread_test_start2);
+  tor_mutex_release(_thread_test_start2);
+
+  if (timedout) {
+    printf("\nTimed out: %d %d", t1_count, t2_count);
+    test_assert(strmap_get(_thread_test_strmap, "thread 1"));
+    test_assert(strmap_get(_thread_test_strmap, "thread 2"));
+    test_assert(!timedout);
+  }
+
+  /* different thread IDs. */
+  test_assert(strcmp(strmap_get(_thread_test_strmap, "thread 1"),
+                     strmap_get(_thread_test_strmap, "thread 2")));
+  test_assert(!strcmp(strmap_get(_thread_test_strmap, "thread 1"),
+                      strmap_get(_thread_test_strmap, "last to run")) ||
+              !strcmp(strmap_get(_thread_test_strmap, "thread 2"),
+                      strmap_get(_thread_test_strmap, "last to run")));
+
+ done:
+  tor_free(s1);
+  tor_free(s2);
+  tor_free(_thread1_name);
+  tor_free(_thread2_name);
+  if (_thread_test_strmap)
+    strmap_free(_thread_test_strmap, NULL);
+  if (_thread_test_start1)
+    tor_mutex_free(_thread_test_start1);
+  if (_thread_test_start2)
+    tor_mutex_free(_thread_test_start2);
+}
+
+/** Run unit tests for compression functions */
+static void
+test_util_gzip(void)
+{
+  char *buf1=NULL, *buf2=NULL, *buf3=NULL, *cp1, *cp2;
+  const char *ccp2;
+  size_t len1, len2;
+  tor_zlib_state_t *state = NULL;
+
+  buf1 = tor_strdup("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZ");
+  test_assert(detect_compression_method(buf1, strlen(buf1)) == UNKNOWN_METHOD);
+  if (is_gzip_supported()) {
+    test_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1,
+                                   GZIP_METHOD));
+    test_assert(buf2);
+    test_assert(!memcmp(buf2, "\037\213", 2)); /* Gzip magic. */
+    test_assert(detect_compression_method(buf2, len1) == GZIP_METHOD);
+
+    test_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1,
+                                     GZIP_METHOD, 1, LOG_INFO));
+    test_assert(buf3);
+    test_streq(buf1,buf3);
+
+    tor_free(buf2);
+    tor_free(buf3);
+  }
+
+  test_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1,
+                                 ZLIB_METHOD));
+  test_assert(buf2);
+  test_assert(!memcmp(buf2, "\x78\xDA", 2)); /* deflate magic. */
+  test_assert(detect_compression_method(buf2, len1) == ZLIB_METHOD);
+
+  test_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1,
+                                   ZLIB_METHOD, 1, LOG_INFO));
+  test_assert(buf3);
+  test_streq(buf1,buf3);
+
+  /* Check whether we can uncompress concatenated, compressed strings. */
+  tor_free(buf3);
+  buf2 = tor_realloc(buf2, len1*2);
+  memcpy(buf2+len1, buf2, len1);
+  test_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1*2,
+                                   ZLIB_METHOD, 1, LOG_INFO));
+  test_eq(len2, (strlen(buf1)+1)*2);
+  test_memeq(buf3,
+             "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZ\0"
+             "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZ\0",
+             (strlen(buf1)+1)*2);
+
+  tor_free(buf1);
+  tor_free(buf2);
+  tor_free(buf3);
+
+  /* Check whether we can uncompress partial strings. */
+  buf1 =
+    tor_strdup("String with low redundancy that won't be compressed much.");
+  test_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1,
+                                 ZLIB_METHOD));
+  tor_assert(len1>16);
+  /* when we allow an incomplete string, we should succeed.*/
+  tor_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1-16,
+                                  ZLIB_METHOD, 0, LOG_INFO));
+  buf3[len2]='\0';
+  tor_assert(len2 > 5);
+  tor_assert(!strcmpstart(buf1, buf3));
+
+  /* when we demand a complete string, this must fail. */
+  tor_free(buf3);
+  tor_assert(tor_gzip_uncompress(&buf3, &len2, buf2, len1-16,
+                                 ZLIB_METHOD, 1, LOG_INFO));
+  tor_assert(!buf3);
+
+  /* Now, try streaming compression. */
+  tor_free(buf1);
+  tor_free(buf2);
+  tor_free(buf3);
+  state = tor_zlib_new(1, ZLIB_METHOD);
+  tor_assert(state);
+  cp1 = buf1 = tor_malloc(1024);
+  len1 = 1024;
+  ccp2 = "ABCDEFGHIJABCDEFGHIJ";
+  len2 = 21;
+  test_assert(tor_zlib_process(state, &cp1, &len1, &ccp2, &len2, 0)
+              == TOR_ZLIB_OK);
+  test_eq(len2, 0); /* Make sure we compressed it all. */
+  test_assert(cp1 > buf1);
+
+  len2 = 0;
+  cp2 = cp1;
+  test_assert(tor_zlib_process(state, &cp1, &len1, &ccp2, &len2, 1)
+              == TOR_ZLIB_DONE);
+  test_eq(len2, 0);
+  test_assert(cp1 > cp2); /* Make sure we really added something. */
+
+  tor_assert(!tor_gzip_uncompress(&buf3, &len2, buf1, 1024-len1,
+                                  ZLIB_METHOD, 1, LOG_WARN));
+  test_streq(buf3, "ABCDEFGHIJABCDEFGHIJ"); /*Make sure it compressed right.*/
+
+ done:
+  if (state)
+    tor_zlib_free(state);
+  tor_free(buf2);
+  tor_free(buf3);
+  tor_free(buf1);
+}
+
+/** Run unit tests for mmap() wrapper functionality. */
+static void
+test_util_mmap(void)
+{
+  char *fname1 = tor_strdup(get_fname("mapped_1"));
+  char *fname2 = tor_strdup(get_fname("mapped_2"));
+  char *fname3 = tor_strdup(get_fname("mapped_3"));
+  const size_t buflen = 17000;
+  char *buf = tor_malloc(17000);
+  tor_mmap_t *mapping = NULL;
+
+  crypto_rand(buf, buflen);
+
+  mapping = tor_mmap_file(fname1);
+  test_assert(! mapping);
+
+  write_str_to_file(fname1, "Short file.", 1);
+  write_bytes_to_file(fname2, buf, buflen, 1);
+  write_bytes_to_file(fname3, buf, 16384, 1);
+
+  mapping = tor_mmap_file(fname1);
+  test_assert(mapping);
+  test_eq(mapping->size, strlen("Short file."));
+  test_streq(mapping->data, "Short file.");
+#ifdef MS_WINDOWS
+  tor_munmap_file(mapping);
+  mapping = NULL;
+  test_assert(unlink(fname1) == 0);
+#else
+  /* make sure we can unlink. */
+  test_assert(unlink(fname1) == 0);
+  test_streq(mapping->data, "Short file.");
+  tor_munmap_file(mapping);
+  mapping = NULL;
+#endif
+
+  /* Now a zero-length file. */
+  write_str_to_file(fname1, "", 1);
+  mapping = tor_mmap_file(fname1);
+  test_eq(mapping, NULL);
+  test_eq(ERANGE, errno);
+  unlink(fname1);
+
+  /* Make sure that we fail to map a no-longer-existent file. */
+  mapping = tor_mmap_file(fname1);
+  test_assert(mapping == NULL);
+
+  /* Now try a big file that stretches across a few pages and isn't aligned */
+  mapping = tor_mmap_file(fname2);
+  test_assert(mapping);
+  test_eq(mapping->size, buflen);
+  test_memeq(mapping->data, buf, buflen);
+  tor_munmap_file(mapping);
+  mapping = NULL;
+
+  /* Now try a big aligned file. */
+  mapping = tor_mmap_file(fname3);
+  test_assert(mapping);
+  test_eq(mapping->size, 16384);
+  test_memeq(mapping->data, buf, 16384);
+  tor_munmap_file(mapping);
+  mapping = NULL;
+
+ done:
+  unlink(fname1);
+  unlink(fname2);
+  unlink(fname3);
+
+  tor_free(fname1);
+  tor_free(fname2);
+  tor_free(fname3);
+  tor_free(buf);
+
+  if (mapping)
+    tor_munmap_file(mapping);
+}
+
+/** Run unit tests for escaping/unescaping data for use by controllers. */
+static void
+test_util_control_formats(void)
+{
+  char *out = NULL;
+  const char *inp =
+    "..This is a test\r\nof the emergency \nbroadcast\r\n..system.\r\nZ.\r\n";
+  size_t sz;
+
+  sz = read_escaped_data(inp, strlen(inp), &out);
+  test_streq(out,
+             ".This is a test\nof the emergency \nbroadcast\n.system.\nZ.\n");
+  test_eq(sz, strlen(out));
+
+ done:
+  tor_free(out);
+}
+
+static void
+test_util_sscanf(void)
+{
+  unsigned u1, u2, u3;
+  char s1[10], s2[10], s3[10], ch;
+  int r;
+
+  r = tor_sscanf("hello world", "hello world"); /* String match: success */
+  test_eq(r, 0);
+  r = tor_sscanf("hello world 3", "hello worlb %u", &u1); /* String fail */
+  test_eq(r, 0);
+  r = tor_sscanf("12345", "%u", &u1); /* Simple number */
+  test_eq(r, 1);
+  test_eq(u1, 12345u);
+  r = tor_sscanf("", "%u", &u1); /* absent number */
+  test_eq(r, 0);
+  r = tor_sscanf("A", "%u", &u1); /* bogus number */
+  test_eq(r, 0);
+  r = tor_sscanf("4294967295", "%u", &u1); /* UINT32_MAX should work. */
+  test_eq(r, 1);
+  test_eq(u1, 4294967295u);
+  r = tor_sscanf("4294967296", "%u", &u1); /* Always say -1 at 32 bits. */
+  test_eq(r, 0);
+  r = tor_sscanf("123456", "%2u%u", &u1, &u2); /* Width */
+  test_eq(r, 2);
+  test_eq(u1, 12u);
+  test_eq(u2, 3456u);
+  r = tor_sscanf("!12:3:456", "!%2u:%2u:%3u", &u1, &u2, &u3); /* separators */
+  test_eq(r, 3);
+  test_eq(u1, 12u);
+  test_eq(u2, 3u);
+  test_eq(u3, 456u);
+  r = tor_sscanf("12:3:045", "%2u:%2u:%3u", &u1, &u2, &u3); /* 0s */
+  test_eq(r, 3);
+  test_eq(u1, 12u);
+  test_eq(u2, 3u);
+  test_eq(u3, 45u);
+  /* %u does not match space.*/
+  r = tor_sscanf("12:3: 45", "%2u:%2u:%3u", &u1, &u2, &u3);
+  test_eq(r, 2);
+  /* %u does not match negative numbers. */
+  r = tor_sscanf("12:3:-4", "%2u:%2u:%3u", &u1, &u2, &u3);
+  test_eq(r, 2);
+  /* Arbitrary amounts of 0-padding are okay */
+  r = tor_sscanf("12:03:000000000000000099", "%2u:%2u:%u", &u1, &u2, &u3);
+  test_eq(r, 3);
+  test_eq(u1, 12u);
+  test_eq(u2, 3u);
+  test_eq(u3, 99u);
+
+  r = tor_sscanf("99% fresh", "%3u%% fresh", &u1); /* percents are scannable.*/
+  test_eq(r, 1);
+  test_eq(u1, 99);
+
+  r = tor_sscanf("hello", "%s", s1); /* %s needs a number. */
+  test_eq(r, -1);
+
+  r = tor_sscanf("hello", "%3s%7s", s1, s2); /* %s matches characters. */
+  test_eq(r, 2);
+  test_streq(s1, "hel");
+  test_streq(s2, "lo");
+  r = tor_sscanf("WD40", "%2s%u", s3, &u1); /* %s%u */
+  test_eq(r, 2);
+  test_streq(s3, "WD");
+  test_eq(u1, 40);
+  r = tor_sscanf("76trombones", "%6u%9s", &u1, s1); /* %u%s */
+  test_eq(r, 2);
+  test_eq(u1, 76);
+  test_streq(s1, "trombones");
+  r = tor_sscanf("hello world", "%9s %9s", s1, s2); /* %s doesn't eat space. */
+  test_eq(r, 2);
+  test_streq(s1, "hello");
+  test_streq(s2, "world");
+  r = tor_sscanf("hi", "%9s%9s%3s", s1, s2, s3); /* %s can be empty. */
+  test_eq(r, 3);
+  test_streq(s1, "hi");
+  test_streq(s2, "");
+  test_streq(s3, "");
+
+  r = tor_sscanf("1.2.3", "%u.%u.%u%c", &u1, &u2, &u3, &ch);
+  test_eq(r, 3);
+  r = tor_sscanf("1.2.3 foobar", "%u.%u.%u%c", &u1, &u2, &u3, &ch);
+  test_eq(r, 4);
+
+ done:
+  ;
+}
+
+/** Run unittests for memory pool allocator */
+static void
+test_util_mempool(void)
+{
+  mp_pool_t *pool = NULL;
+  smartlist_t *allocated = NULL;
+  int i;
+
+  pool = mp_pool_new(1, 100);
+  test_assert(pool);
+  test_assert(pool->new_chunk_capacity >= 100);
+  test_assert(pool->item_alloc_size >= sizeof(void*)+1);
+  mp_pool_destroy(pool);
+  pool = NULL;
+
+  pool = mp_pool_new(241, 2500);
+  test_assert(pool);
+  test_assert(pool->new_chunk_capacity >= 10);
+  test_assert(pool->item_alloc_size >= sizeof(void*)+241);
+  test_eq(pool->item_alloc_size & 0x03, 0);
+  test_assert(pool->new_chunk_capacity < 60);
+
+  allocated = smartlist_create();
+  for (i = 0; i < 20000; ++i) {
+    if (smartlist_len(allocated) < 20 || crypto_rand_int(2)) {
+      void *m = mp_pool_get(pool);
+      memset(m, 0x09, 241);
+      smartlist_add(allocated, m);
+      //printf("%d: %p\n", i, m);
+      //mp_pool_assert_ok(pool);
+    } else {
+      int idx = crypto_rand_int(smartlist_len(allocated));
+      void *m = smartlist_get(allocated, idx);
+      //printf("%d: free %p\n", i, m);
+      smartlist_del(allocated, idx);
+      mp_pool_release(m);
+      //mp_pool_assert_ok(pool);
+    }
+    if (crypto_rand_int(777)==0)
+      mp_pool_clean(pool, 1, 1);
+
+    if (i % 777)
+      mp_pool_assert_ok(pool);
+  }
+
+ done:
+  if (allocated) {
+    SMARTLIST_FOREACH(allocated, void *, m, mp_pool_release(m));
+    mp_pool_assert_ok(pool);
+    mp_pool_clean(pool, 0, 0);
+    mp_pool_assert_ok(pool);
+    smartlist_free(allocated);
+  }
+
+  if (pool)
+    mp_pool_destroy(pool);
+}
+
+/** Run unittests for memory area allocator */
+static void
+test_util_memarea(void)
+{
+  memarea_t *area = memarea_new();
+  char *p1, *p2, *p3, *p1_orig;
+  void *malloced_ptr = NULL;
+  int i;
+
+  test_assert(area);
+
+  p1_orig = p1 = memarea_alloc(area,64);
+  p2 = memarea_alloc_zero(area,52);
+  p3 = memarea_alloc(area,11);
+
+  test_assert(memarea_owns_ptr(area, p1));
+  test_assert(memarea_owns_ptr(area, p2));
+  test_assert(memarea_owns_ptr(area, p3));
+  /* Make sure we left enough space. */
+  test_assert(p1+64 <= p2);
+  test_assert(p2+52 <= p3);
+  /* Make sure we aligned. */
+  test_eq(((uintptr_t)p1) % sizeof(void*), 0);
+  test_eq(((uintptr_t)p2) % sizeof(void*), 0);
+  test_eq(((uintptr_t)p3) % sizeof(void*), 0);
+  test_assert(!memarea_owns_ptr(area, p3+8192));
+  test_assert(!memarea_owns_ptr(area, p3+30));
+  test_assert(tor_mem_is_zero(p2, 52));
+  /* Make sure we don't overalign. */
+  p1 = memarea_alloc(area, 1);
+  p2 = memarea_alloc(area, 1);
+  test_eq(p1+sizeof(void*), p2);
+  {
+    malloced_ptr = tor_malloc(64);
+    test_assert(!memarea_owns_ptr(area, malloced_ptr));
+    tor_free(malloced_ptr);
+  }
+
+  /* memarea_memdup */
+  {
+    malloced_ptr = tor_malloc(64);
+    crypto_rand((char*)malloced_ptr, 64);
+    p1 = memarea_memdup(area, malloced_ptr, 64);
+    test_assert(p1 != malloced_ptr);
+    test_memeq(p1, malloced_ptr, 64);
+    tor_free(malloced_ptr);
+  }
+
+  /* memarea_strdup. */
+  p1 = memarea_strdup(area,"");
+  p2 = memarea_strdup(area, "abcd");
+  test_assert(p1);
+  test_assert(p2);
+  test_streq(p1, "");
+  test_streq(p2, "abcd");
+
+  /* memarea_strndup. */
+  {
+    const char *s = "Ad ogni porta batte la morte e grida: il nome!";
+    /* (From Turandot, act 3.) */
+    size_t len = strlen(s);
+    p1 = memarea_strndup(area, s, 1000);
+    p2 = memarea_strndup(area, s, 10);
+    test_streq(p1, s);
+    test_assert(p2 >= p1 + len + 1);
+    test_memeq(s, p2, 10);
+    test_eq(p2[10], '\0');
+    p3 = memarea_strndup(area, s, len);
+    test_streq(p3, s);
+    p3 = memarea_strndup(area, s, len-1);
+    test_memeq(s, p3, len-1);
+    test_eq(p3[len-1], '\0');
+  }
+
+  memarea_clear(area);
+  p1 = memarea_alloc(area, 1);
+  test_eq(p1, p1_orig);
+  memarea_clear(area);
+
+  /* Check for running over an area's size. */
+  for (i = 0; i < 512; ++i) {
+    p1 = memarea_alloc(area, crypto_rand_int(5)+1);
+    test_assert(memarea_owns_ptr(area, p1));
+  }
+  memarea_assert_ok(area);
+  /* Make sure we can allocate a too-big object. */
+  p1 = memarea_alloc_zero(area, 9000);
+  p2 = memarea_alloc_zero(area, 16);
+  test_assert(memarea_owns_ptr(area, p1));
+  test_assert(memarea_owns_ptr(area, p2));
+
+ done:
+  memarea_drop_all(area);
+  tor_free(malloced_ptr);
+}
+
+/** Run unit tests for utility functions to get file names relative to
+ * the data directory. */
+static void
+test_util_datadir(void)
+{
+  char buf[1024];
+  char *f = NULL;
+  char *temp_dir = NULL;
+
+  temp_dir = get_datadir_fname(NULL);
+  f = get_datadir_fname("state");
+  tor_snprintf(buf, sizeof(buf), "%s"PATH_SEPARATOR"state", temp_dir);
+  test_streq(f, buf);
+  tor_free(f);
+  f = get_datadir_fname2("cache", "thingy");
+  tor_snprintf(buf, sizeof(buf),
+               "%s"PATH_SEPARATOR"cache"PATH_SEPARATOR"thingy", temp_dir);
+  test_streq(f, buf);
+  tor_free(f);
+  f = get_datadir_fname2_suffix("cache", "thingy", ".foo");
+  tor_snprintf(buf, sizeof(buf),
+               "%s"PATH_SEPARATOR"cache"PATH_SEPARATOR"thingy.foo", temp_dir);
+  test_streq(f, buf);
+  tor_free(f);
+  f = get_datadir_fname_suffix("cache", ".foo");
+  tor_snprintf(buf, sizeof(buf), "%s"PATH_SEPARATOR"cache.foo",
+               temp_dir);
+  test_streq(f, buf);
+
+ done:
+  tor_free(f);
+  tor_free(temp_dir);
+}
+
+static void
+test_util_strtok(void)
+{
+  char buf[128];
+  char buf2[128];
+  char *cp1, *cp2;
+  strlcpy(buf, "Graved on the dark in gestures of descent", sizeof(buf));
+  strlcpy(buf2, "they.seemed;their!own;most.perfect;monument", sizeof(buf2));
+  /*  -- "Year's End", Richard Wilbur */
+
+  test_streq("Graved", tor_strtok_r_impl(buf, " ", &cp1));
+  test_streq("they", tor_strtok_r_impl(buf2, ".!..;!", &cp2));
+#define S1() tor_strtok_r_impl(NULL, " ", &cp1)
+#define S2() tor_strtok_r_impl(NULL, ".!..;!", &cp2)
+  test_streq("on", S1());
+  test_streq("the", S1());
+  test_streq("dark", S1());
+  test_streq("seemed", S2());
+  test_streq("their", S2());
+  test_streq("own", S2());
+  test_streq("in", S1());
+  test_streq("gestures", S1());
+  test_streq("of", S1());
+  test_streq("most", S2());
+  test_streq("perfect", S2());
+  test_streq("descent", S1());
+  test_streq("monument", S2());
+  test_assert(NULL == S1());
+  test_assert(NULL == S2());
+ done:
+  ;
+}
+
+#define UTIL_LEGACY(name)                                               \
+  { #name, legacy_test_helper, 0, &legacy_setup, test_util_ ## name }
+
+struct testcase_t util_tests[] = {
+  UTIL_LEGACY(time),
+  UTIL_LEGACY(config_line),
+  UTIL_LEGACY(strmisc),
+  UTIL_LEGACY(pow2),
+  UTIL_LEGACY(gzip),
+  UTIL_LEGACY(datadir),
+  UTIL_LEGACY(mempool),
+  UTIL_LEGACY(memarea),
+  UTIL_LEGACY(control_formats),
+  UTIL_LEGACY(mmap),
+  UTIL_LEGACY(threads),
+  UTIL_LEGACY(sscanf),
+  UTIL_LEGACY(strtok),
+  END_OF_TESTCASES
+};
+


Property changes on: tor/trunk/src/test/test_util.c
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Added: tor/trunk/src/test/tinytest.c
===================================================================
--- tor/trunk/src/test/tinytest.c	                        (rev 0)
+++ tor/trunk/src/test/tinytest.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,369 @@
+/* tinytest.c -- Copyright 2009 Nick Mathewson
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#endif
+
+#ifndef __GNUC__
+#define __attribute__(x)
+#endif
+
+#include "tinytest.h"
+#include "tinytest_macros.h"
+
+#define LONGEST_TEST_NAME 16384
+
+static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/
+static int n_ok = 0; /**< Number of tests that have passed */
+static int n_bad = 0; /**< Number of tests that have failed. */
+static int n_skipped = 0; /**< Number of tests that have been skipped. */
+
+static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/
+static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */
+static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */
+const char *verbosity_flag = "";
+
+enum outcome { SKIP=2, OK=1, FAIL=0 };
+static enum outcome cur_test_outcome = 0;
+const char *cur_test_prefix = NULL; /**< prefix of the current test group */
+/** Name of the  current test, if we haven't logged is yet. Used for --quiet */
+const char *cur_test_name = NULL;
+
+#ifdef WIN32
+/** Pointer to argv[0] for win32. */
+static const char *commandname = NULL;
+#endif
+
+static void usage(struct testgroup_t *groups, int list_groups)
+  __attribute__((noreturn));
+
+static enum outcome
+_testcase_run_bare(const struct testcase_t *testcase)
+{
+	void *env = NULL;
+	int outcome;
+	if (testcase->setup) {
+		env = testcase->setup->setup_fn(testcase);
+                if (!env)
+			return FAIL;
+		else if (env == (void*)TT_SKIP)
+			return SKIP;
+	}
+
+	cur_test_outcome = OK;
+	testcase->fn(env);
+	outcome = cur_test_outcome;
+
+	if (testcase->setup) {
+		if (testcase->setup->cleanup_fn(testcase, env) == 0)
+			outcome = FAIL;
+	}
+
+	return outcome;
+}
+
+#define MAGIC_EXITCODE 42
+
+static enum outcome
+_testcase_run_forked(const struct testgroup_t *group,
+		     const struct testcase_t *testcase)
+{
+#ifdef WIN32
+	/* Fork? On Win32?  How primitive!  We'll do what the smart kids do:
+	   we'll invoke our own exe (whose name we recall from the command
+	   line) with a command line that tells it to run just the test we
+	   want, and this time without forking.
+
+	   (No, threads aren't an option.  The whole point of forking is to
+	   share no state between tests.)
+	 */
+	int ok;
+	char buffer[LONGEST_TEST_NAME+256];
+	STARTUPINFO si;
+	PROCESS_INFORMATION info;
+	DWORD exitcode;
+
+	if (!in_tinytest_main) {
+		printf("\nERROR.  On Windows, _testcase_run_forked must be"
+		       " called from within tinytest_main.\n");
+		abort();
+	}
+	if (opt_verbosity>0)
+		printf("[forking] ");
+
+	snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s",
+		 commandname, verbosity_flag, group->prefix, testcase->name);
+
+	memset(&si, 0, sizeof(si));
+	memset(&info, 0, sizeof(info));
+	si.cb = sizeof(si);
+
+	ok = CreateProcess(commandname, buffer, NULL, NULL, 0,
+			   0, NULL, NULL, &si, &info);
+	if (!ok) {
+		printf("CreateProcess failed!\n");
+		return 0;
+	}
+	WaitForSingleObject(info.hProcess, INFINITE);
+	GetExitCodeProcess(info.hProcess, &exitcode);
+	CloseHandle(info.hProcess);
+	CloseHandle(info.hThread);
+	if (exitcode == 0)
+		return OK;
+	else if (exitcode == MAGIC_EXITCODE)
+		return SKIP;
+	else
+		return FAIL;
+#else
+	int outcome_pipe[2];
+	pid_t pid;
+        (void)group;
+
+	if (pipe(outcome_pipe))
+		perror("opening pipe");
+
+	if (opt_verbosity>0)
+		printf("[forking] ");
+	pid = fork();
+	if (!pid) {
+		/* child. */
+		int test_r, write_r;
+		char b[1];
+		close(outcome_pipe[0]);
+		test_r = _testcase_run_bare(testcase);
+		assert(0<=(int)test_r && (int)test_r<=2);
+		b[0] = "NYS"[test_r];
+	        write_r = (int)write(outcome_pipe[1], b, 1);
+		if (write_r != 1) {
+			perror("write outcome to pipe");
+			exit(1);
+		}
+		exit(0);
+	} else {
+		/* parent */
+		int status, r;
+		char b[1];
+		/* Close this now, so that if the other side closes it,
+		 * our read fails. */
+		close(outcome_pipe[1]);
+		r = (int)read(outcome_pipe[0], b, 1);
+		if (r == 0) {
+			printf("[Lost connection!] ");
+			return 0;
+		} else if (r != 1) {
+			perror("read outcome from pipe");
+		}
+		waitpid(pid, &status, 0);
+		close(outcome_pipe[0]);
+		return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL);
+	}
+#endif
+}
+
+int
+testcase_run_one(const struct testgroup_t *group,
+		 const struct testcase_t *testcase)
+{
+	enum outcome outcome;
+
+	if (testcase->flags & TT_SKIP) {
+		if (opt_verbosity>0)
+			printf("%s%s: SKIPPED\n",
+			    group->prefix, testcase->name);
+		++n_skipped;
+		return SKIP;
+	}
+
+	if (opt_verbosity>0 && !opt_forked) {
+		printf("%s%s: ", group->prefix, testcase->name);
+	} else {
+		if (opt_verbosity==0) printf(".");
+		cur_test_prefix = group->prefix;
+		cur_test_name = testcase->name;
+	}
+
+	if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) {
+		outcome = _testcase_run_forked(group, testcase);
+	} else {
+		outcome  = _testcase_run_bare(testcase);
+	}
+
+	if (outcome == OK) {
+		++n_ok;
+		if (opt_verbosity>0 && !opt_forked)
+			puts(opt_verbosity==1?"OK":"");
+	} else if (outcome == SKIP) {
+		++n_skipped;
+		if (opt_verbosity>0 && !opt_forked)
+			puts("SKIPPED");
+	} else {
+		++n_bad;
+		if (!opt_forked)
+			printf("\n  [%s FAILED]\n", testcase->name);
+	}
+
+	if (opt_forked) {
+		exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1));
+	} else {
+		return (int)outcome;
+	}
+}
+
+int
+_tinytest_set_flag(struct testgroup_t *groups, const char *arg, unsigned long flag)
+{
+	int i, j;
+	size_t length = LONGEST_TEST_NAME;
+	char fullname[LONGEST_TEST_NAME];
+	int found=0;
+	if (strstr(arg, ".."))
+		length = strstr(arg,"..")-arg;
+	for (i=0; groups[i].prefix; ++i) {
+		for (j=0; groups[i].cases[j].name; ++j) {
+			snprintf(fullname, sizeof(fullname), "%s%s",
+				 groups[i].prefix, groups[i].cases[j].name);
+			if (!flag) /* Hack! */
+				printf("    %s\n", fullname);
+			if (!strncmp(fullname, arg, length)) {
+				groups[i].cases[j].flags |= flag;
+				++found;
+			}
+		}
+	}
+	return found;
+}
+
+static void
+usage(struct testgroup_t *groups, int list_groups)
+{
+	puts("Options are: [--verbose|--quiet|--terse] [--no-fork]");
+	puts("  Specify tests by name, or using a prefix ending with '..'");
+	puts("  Use --list-tests for a list of tests.");
+	if (list_groups) {
+		puts("Known tests are:");
+		_tinytest_set_flag(groups, "..", 0);
+	}
+	exit(0);
+}
+
+int
+tinytest_main(int c, const char **v, struct testgroup_t *groups)
+{
+	int i, j, n=0;
+
+#ifdef WIN32
+	commandname = v[0];
+#endif
+	for (i=1; i<c; ++i) {
+		if (v[i][0] == '-') {
+			if (!strcmp(v[i], "--RUNNING-FORKED")) {
+				opt_forked = 1;
+			} else if (!strcmp(v[i], "--no-fork")) {
+				opt_nofork = 1;
+			} else if (!strcmp(v[i], "--quiet")) {
+				opt_verbosity = -1;
+				verbosity_flag = "--quiet";
+			} else if (!strcmp(v[i], "--verbose")) {
+				opt_verbosity = 2;
+				verbosity_flag = "--verbose";
+			} else if (!strcmp(v[i], "--terse")) {
+				opt_verbosity = 0;
+				verbosity_flag = "--terse";
+			} else if (!strcmp(v[i], "--help")) {
+				usage(groups, 0);
+			} else if (!strcmp(v[i], "--list-tests")) {
+				usage(groups, 1);
+			} else {
+				printf("Unknown option %s.  Try --help\n",v[i]);
+				return -1;
+			}
+		} else {
+			++n;
+			if (!_tinytest_set_flag(groups, v[i], _TT_ENABLED)) {
+				printf("No such test as %s!\n", v[i]);
+				return -1;
+			}
+		}
+	}
+	if (!n)
+		_tinytest_set_flag(groups, "..", _TT_ENABLED);
+
+	setvbuf(stdout, NULL, _IONBF, 0);
+
+	++in_tinytest_main;
+	for (i=0; groups[i].prefix; ++i)
+		for (j=0; groups[i].cases[j].name; ++j)
+			if (groups[i].cases[j].flags & _TT_ENABLED)
+				testcase_run_one(&groups[i],
+						 &groups[i].cases[j]);
+
+	--in_tinytest_main;
+
+	if (opt_verbosity==0)
+		puts("");
+
+	if (n_bad)
+		printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad,
+		       n_bad+n_ok,n_skipped);
+	else if (opt_verbosity >= 1)
+		printf("%d tests ok.  (%d skipped)\n", n_ok, n_skipped);
+
+	return (n_bad == 0) ? 0 : 1;
+}
+
+int
+_tinytest_get_verbosity(void)
+{
+	return opt_verbosity;
+}
+
+void
+_tinytest_set_test_failed(void)
+{
+	if (opt_verbosity <= 0 && cur_test_name) {
+		if (opt_verbosity==0) puts("");
+		printf("%s%s: ", cur_test_prefix, cur_test_name);
+		cur_test_name = NULL;
+	}
+	cur_test_outcome = 0;
+}
+
+void
+_tinytest_set_test_skipped(void)
+{
+	if (cur_test_outcome==OK)
+		cur_test_outcome = SKIP;
+}
+


Property changes on: tor/trunk/src/test/tinytest.c
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Added: tor/trunk/src/test/tinytest.h
===================================================================
--- tor/trunk/src/test/tinytest.h	                        (rev 0)
+++ tor/trunk/src/test/tinytest.h	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,87 @@
+/* tinytest.h -- Copyright 2009 Nick Mathewson
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TINYTEST_H
+#define _TINYTEST_H
+
+/** Flag for a test that needs to run in a subprocess. */
+#define TT_FORK  (1<<0)
+/** Runtime flag for a test we've decided to skip. */
+#define TT_SKIP  (1<<1)
+/** Internal runtime flag for a test we've decided to run. */
+#define _TT_ENABLED  (1<<2)
+/** If you add your own flags, make them start at this point. */
+#define TT_FIRST_USER_FLAG (1<<3)
+
+typedef void (*testcase_fn)(void *);
+
+struct testcase_t;
+
+/** Functions to initialize/teardown a structure for a testcase. */
+struct testcase_setup_t {
+	/** Return a new structure for use by a given testcase. */
+	void *(*setup_fn)(const struct testcase_t *);
+	/** Clean/free a structure from setup_fn. Return 1 if ok, 0 on err. */
+	int (*cleanup_fn)(const struct testcase_t *, void *);
+};
+
+/** A single test-case that you can run. */
+struct testcase_t {
+	const char *name; /**< An identifier for this case. */
+	testcase_fn fn; /**< The function to run to implement this case. */
+	unsigned long flags; /**< Bitfield of TT_* flags. */
+	const struct testcase_setup_t *setup; /**< Optional setup/cleanup fns*/
+	void *setup_data; /**< Extra data usable by setup function */
+};
+#define END_OF_TESTCASES { NULL, NULL, 0, NULL, NULL }
+
+/** A group of tests that are selectable together. */
+struct testgroup_t {
+	const char *prefix; /**< Prefix to prepend to testnames. */
+	struct testcase_t *cases; /** Array, ending with END_OF_TESTCASES */
+};
+#define END_OF_GROUPS { NULL, NULL}
+
+/** Implementation: called from a test to indicate failure, before logging. */
+void _tinytest_set_test_failed(void);
+/** Implementation: called from a test to indicate that we're skipping. */
+void _tinytest_set_test_skipped(void);
+/** Implementation: return 0 for quiet, 1 for normal, 2 for loud. */
+int _tinytest_get_verbosity(void);
+/** Implementation: Set a flag on tests matching a name; returns number
+ * of tests that matched. */
+int _tinytest_set_flag(struct testgroup_t *, const char *, unsigned long);
+
+/** Set all tests in 'groups' matching the name 'named' to be skipped. */
+#define tinytest_skip(groups, named) \
+	_tinytest_set_flag(groups, named, TT_SKIP)
+
+/** Run a single testcase in a single group. */
+int testcase_run_one(const struct testgroup_t *,const struct testcase_t *);
+/** Run a set of testcases from an END_OF_GROUPS-terminated array of groups,
+    as selected from the command line. */
+int tinytest_main(int argc, const char **argv, struct testgroup_t *groups);
+
+#endif


Property changes on: tor/trunk/src/test/tinytest.h
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Added: tor/trunk/src/test/tinytest_demo.c
===================================================================
--- tor/trunk/src/test/tinytest_demo.c	                        (rev 0)
+++ tor/trunk/src/test/tinytest_demo.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,215 @@
+/* tinytest_demo.c -- Copyright 2009 Nick Mathewson
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/* Welcome to the example file for tinytest!  I'll show you how to set up
+ * some simple and not-so-simple testcases. */
+
+/* Make sure you include these headers. */
+#include "tinytest.h"
+#include "tinytest_macros.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/* ============================================================ */
+
+/* First, let's see if strcmp is working.  (All your test cases should be
+ * functions declared to take a single void * as) an argument. */
+void
+test_strcmp(void *data)
+{
+	(void)data; /* This testcase takes no data. */
+
+	/* Let's make sure the empty string is equal to itself */
+	if (strcmp("","")) {
+		/* This macro tells tinytest to stop the current test
+		 * and go straight to the "end" label. */
+		tt_abort_msg("The empty string was not equal to itself");
+	}
+
+	/* Pretty often, calling tt_abort_msg to indicate failure is more
+	   heavy-weight than you want.  Instead, just say: */
+	tt_assert(strcmp("testcase", "testcase") == 0);
+
+	/* Occasionally, you don't want to stop the current testcase just
+	   because a single assertion has failed.  In that case, use
+	   tt_want: */
+	tt_want(strcmp("tinytest", "testcase") > 0);
+
+	/* You can use the tt_*_op family of macros to compare values and to
+	   fail unless they have the relationship you want.  They produce
+	   more useful output than tt_assert, since they display the actual
+	   values of the failing things.
+
+	   Fail unless strcmp("abc, "abc") == 0 */
+	tt_int_op(strcmp("abc", "abc"), ==, 0);
+
+	/* Fail unless strcmp("abc, "abcd") is less than 0 */
+	tt_int_op(strcmp("abc", "abcd"), < , 0);
+
+	/* Incidentally, there's a test_str_op that uses strcmp internally. */
+	tt_str_op("abc", <, "abcd");
+
+
+	/* Every test-case function needs to finish with an "end:"
+	   label and (optionally) code to clean up local variables. */
+ end:
+	;
+}
+
+/* ============================================================ */
+
+/* Now let's mess with setup and teardown functions!  These are handy if
+   you have a bunch of tests that all need a similar environment, and you
+   wnat to reconstruct that environment freshly for each one. */
+
+/* First you declare a type to hold the environment info, and functions to
+   set it up and tear it down. */
+struct data_buffer {
+	/* We're just going to have couple of character buffer.  Using
+	   setup/teardown functions is probably overkill for this case.
+
+	   You could also do file descriptors, complicated handles, temporary
+	   files, etc. */
+	char buffer1[512];
+	char buffer2[512];
+};
+/* The setup function needs to take a const struct testcase_t and return
+   void* */
+void *
+setup_data_buffer(const struct testcase_t *testcase)
+{
+	struct data_buffer *db = malloc(sizeof(struct data_buffer));
+
+	/* If you had a complicated set of setup rules, you might behave
+	   differently here depending on testcase->flags or
+	   testcase->setup_data or even or testcase->name. */
+
+	/* Returning a NULL here would mean that we couldn't set up for this
+	   test, so we don't need to test db for null. */
+	return db;
+}
+/* The clean function deallocates storage carefully and returns true on
+   success. */
+int
+clean_data_buffer(const struct testcase_t *testcase, void *ptr)
+{
+	struct data_buffer *db = ptr;
+
+	if (db) {
+		free(db);
+		return 1;
+	}
+	return 0;
+}
+/* Finally, declare a testcase_setup_t with these functions. */
+struct testcase_setup_t data_buffer_setup = {
+	setup_data_buffer, clean_data_buffer
+};
+
+
+/* Now let's write our test. */
+void
+test_memcpy(void *ptr)
+{
+	/* This time, we use the argument. */
+	struct data_buffer *db = ptr;
+
+	/* We'll also introduce a local variable that might need cleaning up. */
+	char *mem = NULL;
+
+	/* Let's make sure that memcpy does what we'd like. */
+	strcpy(db->buffer1, "String 0");
+	memcpy(db->buffer2, db->buffer1, sizeof(db->buffer1));
+	tt_str_op(db->buffer1, ==, db->buffer2);
+
+	/* Now we've allocated memory that's referenced by a local variable.
+	   The end block of the function will clean it up. */
+	mem = strdup("Hello world.");
+	tt_assert(mem);
+
+	/* Another rather trivial test. */
+	tt_str_op(db->buffer1, !=, mem);
+
+ end:
+	/* This time our end block has something to do. */
+	if (mem)
+		free(mem);
+}
+
+/* ============================================================ */
+
+/* Now we need to make sure that our tests get invoked.   First, you take
+   a bunch of related tests and put them into an array of struct testcase_t.
+*/
+
+struct testcase_t demo_tests[] = {
+	/* Here's a really simple test: it has a name you can refer to it
+	   with, and a function to invoke it. */
+	{ "strcmp", test_strcmp, },
+
+	/* The second test has a flag, "TT_FORK", to make it run in a
+	   subprocess, and a pointer to the testcase_setup_t that configures
+	   its environment. */
+	{ "memcpy", test_memcpy, TT_FORK, &data_buffer_setup },
+
+	/* The array has to end with END_OF_TESTCASES. */
+	END_OF_TESTCASES
+};
+
+/* Next, we make an array of testgroups.  This is mandatory.  Unlike more
+   heavy-duty testing frameworks, groups can't nest. */
+struct testgroup_t groups[] = {
+
+	/* Every group has a 'prefix', and an array of tests.  That's it. */
+	{ "demo/", demo_tests },
+
+        END_OF_GROUPS
+};
+
+
+int
+main(int c, const char **v)
+{
+	/* Finally, just call tinytest_main().  It lets you specify verbose
+	   or quiet output with --verbose and --quiet.  You can list
+	   specific tests:
+
+	       tinytest-demo demo/memcpy
+
+	   or use a ..-wildcard to select multiple tests with a common
+	   prefix:
+
+	       tinytest-demo demo/..
+
+	   If you list no tests, you get them all by default, so that
+	   "tinytest-demo" and "tinytest-demo .." mean the same thing.
+
+	*/
+	return tinytest_main(c, v, groups);
+}


Property changes on: tor/trunk/src/test/tinytest_demo.c
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Added: tor/trunk/src/test/tinytest_macros.h
===================================================================
--- tor/trunk/src/test/tinytest_macros.h	                        (rev 0)
+++ tor/trunk/src/test/tinytest_macros.h	2009-09-25 17:06:41 UTC (rev 20669)
@@ -0,0 +1,167 @@
+/* tinytest_macros.h -- Copyright 2009 Nick Mathewson
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TINYTEST_MACROS_H
+#define _TINYTEST_MACROS_H
+
+/* Helpers for defining statement-like macros */
+#define TT_STMT_BEGIN do {
+#define TT_STMT_END } while(0)
+
+/* Redefine this if your test functions want to abort with something besides
+ * "goto end;" */
+#ifndef TT_EXIT_TEST_FUNCTION
+#define TT_EXIT_TEST_FUNCTION TT_STMT_BEGIN goto end; TT_STMT_END
+#endif
+
+/* Redefine this if you want to note success/failure in some different way. */
+#ifndef TT_DECLARE
+#define TT_DECLARE(prefix, args)				\
+	TT_STMT_BEGIN						\
+	printf("\n  %s %s:%d: ",prefix,__FILE__,__LINE__);	\
+	printf args ;						\
+	TT_STMT_END
+#endif
+
+/* Announce a failure.  Args are parenthesized printf args. */
+#define TT_GRIPE(args) TT_DECLARE("FAIL", args)
+
+/* Announce a non-failure if we're verbose. */
+#define TT_BLATHER(args)						\
+	TT_STMT_BEGIN							\
+	if (_tinytest_get_verbosity()>1) TT_DECLARE("  OK", args);	\
+	TT_STMT_END
+
+#define TT_DIE(args)						\
+	TT_STMT_BEGIN						\
+	_tinytest_set_test_failed();				\
+	TT_GRIPE(args);						\
+	TT_EXIT_TEST_FUNCTION;					\
+	TT_STMT_END
+
+#define TT_FAIL(args)				\
+	TT_STMT_BEGIN						\
+	_tinytest_set_test_failed();				\
+	TT_GRIPE(args);						\
+	TT_STMT_END
+
+/* Fail and abort the current test for the reason in msg */
+#define tt_abort_printf(msg) TT_DIE(msg)
+#define tt_abort_perror(op) TT_DIE(("%s: %s [%d]",(op),strerror(errno), errno))
+#define tt_abort_msg(msg) TT_DIE(("%s", msg))
+#define tt_abort() TT_DIE(("%s", "(Failed.)"))
+
+/* Fail but do not abort the current test for the reason in msg. */
+#define tt_fail_printf(msg) TT_FAIL(msg)
+#define tt_fail_perror(op) TT_FAIL(("%s: %s [%d]",(op),strerror(errno), errno))
+#define tt_fail_msg(msg) TT_FAIL(("%s", msg))
+#define tt_fail() TT_FAIL(("%s", "(Failed.)"))
+
+/* End the current test, and indicate we are skipping it. */
+#define tt_skip()                               \
+	TT_STMT_BEGIN						\
+	_tinytest_set_test_skipped();				\
+	TT_EXIT_TEST_FUNCTION;					\
+	TT_STMT_END
+
+#define _tt_want(b, msg, fail)				\
+	TT_STMT_BEGIN					\
+	if (!(b)) {					\
+		_tinytest_set_test_failed();		\
+		TT_GRIPE((msg));			\
+		fail;					\
+	} else {					\
+		TT_BLATHER((msg));			\
+	}						\
+	TT_STMT_END
+
+/* Assert b, but do not stop the test if b fails.  Log msg on failure. */
+#define tt_want_msg(b, msg)			\
+	_tt_want(b, msg, );
+
+/* Assert b and stop the test if b fails.  Log msg on failure. */
+#define tt_assert_msg(b, msg)			\
+	_tt_want(b, msg, TT_EXIT_TEST_FUNCTION);
+
+/* Assert b, but do not stop the test if b fails. */
+#define tt_want(b)   tt_want_msg( (b), "want("#b")")
+/* Assert b, and stop the test if b fails. */
+#define tt_assert(b) tt_assert_msg((b), "assert("#b")")
+
+#define tt_assert_test_fmt_type(a,b,str_test,type,test,printf_type,printf_fmt, \
+                                setup_block,cleanup_block)              \
+	TT_STMT_BEGIN							\
+	type _val1 = (type)(a);						\
+	type _val2 = (type)(b);						\
+	int _tt_status = (test);					\
+	if (!_tt_status || _tinytest_get_verbosity()>1)	{		\
+		printf_type _print;					\
+		printf_type _print1;					\
+		printf_type _print2;					\
+		type _value = _val1;					\
+		setup_block;						\
+		_print1 = _print;					\
+		_value = _val2;						\
+		setup_block;						\
+		_print2 = _print;					\
+		TT_DECLARE(_tt_status?"  OK":"FAIL",			\
+			   ("assert(%s): "printf_fmt" vs "printf_fmt,	\
+			    str_test, _print1, _print2));		\
+		_print = _print1;					\
+		cleanup_block;						\
+		_print = _print2;					\
+		cleanup_block;						\
+		if (!_tt_status) {					\
+			_tinytest_set_test_failed();			\
+			TT_EXIT_TEST_FUNCTION;				\
+		}							\
+	}								\
+	TT_STMT_END
+
+#define tt_assert_test_type(a,b,str_test,type,test,fmt)			\
+	tt_assert_test_fmt_type(a,b,str_test,type,test,type,fmt,	\
+				{_print=_value;},{})
+
+/* Helper: assert that a op b, when cast to type.  Format the values with
+ * printf format fmt on failure. */
+#define tt_assert_op_type(a,op,b,type,fmt)				\
+	tt_assert_test_type(a,b,#a" "#op" "#b,type,(_val1 op _val2),fmt)
+
+#define tt_int_op(a,op,b)			\
+	tt_assert_test_type(a,b,#a" "#op" "#b,long,(_val1 op _val2),"%ld")
+
+#define tt_uint_op(a,op,b)						\
+	tt_assert_test_type(a,b,#a" "#op" "#b,unsigned long,		\
+			    (_val1 op _val2),"%lu")
+
+#define tt_ptr_op(a,op,b)						\
+	tt_assert_test_type(a,b,#a" "#op" "#b,void*,			\
+			    (_val1 op _val2),"%p")
+
+#define tt_str_op(a,op,b)						\
+	tt_assert_test_type(a,b,#a" "#op" "#b,const char *,		\
+			    (strcmp(_val1,_val2) op 0),"<%s>")
+
+#endif


Property changes on: tor/trunk/src/test/tinytest_macros.h
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision

Modified: tor/trunk/src/tools/tor-gencert.c
===================================================================
--- tor/trunk/src/tools/tor-gencert.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/tools/tor-gencert.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -70,7 +70,7 @@
 static void
 crypto_log_errors(int severity, const char *doing)
 {
-  unsigned int err;
+  unsigned long err;
   const char *msg, *lib, *func;
   while ((err = ERR_get_error()) != 0) {
     msg = (const char*)ERR_reason_error_string(err);
@@ -94,7 +94,7 @@
 {
   char *cp;
   char buf[1024]; /* "Ought to be enough for anybody." */
-  int n = read_all(passphrase_fd, buf, sizeof(buf), 0);
+  ssize_t n = read_all(passphrase_fd, buf, sizeof(buf), 0);
   if (n < 0) {
     log_err(LD_GENERAL, "Couldn't read from passphrase fd: %s",
             strerror(errno));

Modified: tor/trunk/src/tools/tor-resolve.c
===================================================================
--- tor/trunk/src/tools/tor-resolve.c	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/tools/tor-resolve.c	2009-09-25 17:06:41 UTC (rev 20669)
@@ -51,7 +51,7 @@
 /** Set *<b>out</b> to a newly allocated SOCKS4a resolve request with
  * <b>username</b> and <b>hostname</b> as provided.  Return the number
  * of bytes in the request. */
-static int
+static ssize_t
 build_socks_resolve_request(char **out,
                             const char *username,
                             const char *hostname,
@@ -184,7 +184,7 @@
   int s;
   struct sockaddr_in socksaddr;
   char *req = NULL;
-  int len = 0;
+  ssize_t len = 0;
 
   tor_assert(hostname);
   tor_assert(result_addr);

Modified: tor/trunk/src/win32/orconfig.h
===================================================================
--- tor/trunk/src/win32/orconfig.h	2009-09-25 16:25:40 UTC (rev 20668)
+++ tor/trunk/src/win32/orconfig.h	2009-09-25 17:06:41 UTC (rev 20669)
@@ -226,5 +226,5 @@
 #define USING_TWOS_COMPLEMENT
 
 /* Version number of package */
-#define VERSION "0.2.2.0-alpha-dev"
+#define VERSION "0.2.2.3-alpha-dev"
 



More information about the tor-commits mailing list