diff --git a/ChangeLog b/ChangeLog
index 891b66703688eeabe95bda244ba7b1a91a8f836b..f479444162be306b72374626caeeb0e158e56511 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,62 @@
+Changes in version 0.2.2.11-alpha - 2010-04-15
+  o Major bugfixes:
+    - Directory mirrors were fetching relay descriptors only from v2
+      directory authorities, rather than v3 authorities like they should.
+      Only 2 v2 authorities remain (compared to 7 v3 authorities), leading
+      to a serious bottleneck. Bugfix on 0.2.0.9-alpha. Fixes bug 1324.
+    - Fix a parsing error that made every possible value of
+      CircPriorityHalflifeMsec get treated as "1 msec". Bugfix
+      on 0.2.2.7-alpha. Rename CircPriorityHalflifeMsec to
+      CircuitPriorityHalflifeMsec, so authorities can tell newer relays
+      about the option without breaking older ones.
+    - Fix SSL renegotiation behavior on OpenSSL versions like on Centos
+      that claim to be earlier than 0.9.8m, but which have in reality
+      backported huge swaths of 0.9.8m or 0.9.8n renegotiation
+      behavior. Possible fix for some cases of bug 1346.
+
+  o Minor features:
+    - Experiment with a more aggressive approach to preventing clients
+      from making one-hop exit streams. Exit relays who want to try it
+      out can set "RefuseUnknownExits 1" in their torrc, and then look
+      for "Attempt by %s to open a stream" log messages. Let us know
+      how it goes!
+    - Add support for statically linking zlib by specifying
+      --enable-static-zlib, to go with our support for statically linking
+      openssl and libevent. Resolves bug 1358.
+
+  o Minor bugfixes:
+    - Fix a segfault that happens whenever a Tor client that is using
+      libevent2's bufferevents gets a hup signal. Bugfix on 0.2.2.5-alpha;
+      fixes bug 1341.
+    - When we cleaned up the contrib/tor-exit-notice.html file, we left
+      out the first line. Fixes bug 1295.
+    - When building the manpage from a tarball, we required asciidoc, but
+      the asciidoc -> roff/html conversion was already done for the
+      tarball. Make 'make' complain only when we need asciidoc (either
+      because we're compiling directly from git, or because we altered
+      the asciidoc manpage in the tarball). Bugfix on 0.2.2.9-alpha.
+    - When none of the directory authorities vote on any params, Tor
+      segfaulted when trying to make the consensus from the votes. We
+      didn't trigger the bug in practice, because authorities do include
+      params in their votes. Bugfix on 0.2.2.10-alpha; fixes bug 1322.
+
+  o Testsuite fixes:
+    - In the util/threads test, no longer free the test_mutex before all
+      worker threads have finished. Bugfix on 0.2.1.6-alpha.
+    - The master thread could starve the worker threads quite badly on
+      certain systems, causing them to run only partially in the allowed
+      window. This resulted in test failures. Now the master thread sleeps
+      occasionally for a few microseconds while the two worker-threads
+      compete for the mutex. Bugfix on 0.2.0.1-alpha.
+
+
 Changes in version 0.2.2.10-alpha - 2010-03-07
+  Tor 0.2.2.10-alpha fixes a regression introduced in 0.2.2.9-alpha that
+  could prevent relays from guessing their IP address correctly. It also
+  starts the groundwork for another client-side performance boost, since
+  currently we're not making efficient use of relays that have both the
+  Guard flag and the Exit flag.
+
   o Major bugfixes:
     - Fix a regression from our patch for bug 1244 that caused relays
       to guess their IP address incorrectly if they didn't set Address
@@ -53,6 +111,9 @@ Changes in version 0.2.2.10-alpha - 2010-03-07
 
 
 Changes in version 0.2.2.9-alpha - 2010-02-22
+  Tor 0.2.2.9-alpha makes Tor work again on the latest OS X, updates the
+  location of a directory authority, and cleans up a bunch of small bugs.
+
   o Directory authority changes:
     - Change IP address for dannenberg (v3 directory authority), and
       remove moria2 (obsolete v1, v2 directory authority and v0 hidden
@@ -133,7 +194,11 @@ Changes in version 0.2.2.9-alpha - 2010-02-22
       open() without checking it.
 
 
-Changes in version 0.2.1.25 - 2010-03-??
+Changes in version 0.2.1.25 - 2010-03-16
+  Tor 0.2.1.25 fixes a regression introduced in 0.2.1.23 that could
+  prevent relays from guessing their IP address correctly. It also fixes
+  several minor potential security bugs.
+
   o Major bugfixes:
     - Fix a regression from our patch for bug 1244 that caused relays
       to guess their IP address incorrectly if they didn't set Address
@@ -208,6 +273,10 @@ Changes in version 0.2.1.23 - 2010-02-13
 
 
 Changes in version 0.2.2.8-alpha - 2010-01-26
+  Tor 0.2.2.8-alpha fixes a crash bug in 0.2.2.7-alpha that has been
+  causing bridge relays to disappear. If you're running a bridge,
+  please upgrade.
+
   o Major bugfixes:
     - Fix a memory corruption bug on bridges that occured during the
       inclusion of stats data in extra-info descriptors. Also fix the
diff --git a/ReleaseNotes b/ReleaseNotes
index ee0097a787343ec433140e56aee94b16c0583103..daac38c44a533eff9d9d1e43a96d531521a66d3e 100644
--- a/ReleaseNotes
+++ b/ReleaseNotes
@@ -3,6 +3,30 @@ This document summarizes new features and bugfixes in each stable release
 of Tor. If you want to see more detailed descriptions of the changes in
 each development snapshot, see the ChangeLog file.
 
+Changes in version 0.2.1.25 - 2010-03-16
+  Tor 0.2.1.25 fixes a regression introduced in 0.2.1.23 that could
+  prevent relays from guessing their IP address correctly. It also fixes
+  several minor potential security bugs.
+
+  o Major bugfixes:
+    - Fix a regression from our patch for bug 1244 that caused relays
+      to guess their IP address incorrectly if they didn't set Address
+      in their torrc and/or their address fails to resolve. Bugfix on
+      0.2.1.23; fixes bug 1269.
+    - When freeing a session key, zero it out completely. We only zeroed
+      the first ptrsize bytes. Bugfix on 0.0.2pre8. Discovered and
+      patched by ekir. Fixes bug 1254.
+
+  o Minor bugfixes:
+    - Fix a dereference-then-NULL-check sequence when publishing
+      descriptors. Bugfix on 0.2.1.5-alpha. Discovered by ekir; fixes
+      bug 1255.
+    - Fix another dereference-then-NULL-check sequence. Bugfix on
+      0.2.1.14-rc. Discovered by ekir; fixes bug 1256.
+    - Make sure we treat potentially not NUL-terminated strings correctly.
+      Bugfix on 0.1.1.13-alpha. Discovered by rieo; fixes bug 1257.
+
+
 Changes in version 0.2.1.24 - 2010-02-21
   Tor 0.2.1.24 makes Tor work again on the latest OS X -- this time
   for sure!
diff --git a/configure.in b/configure.in
index 4f11f16dbfb2d862b36f9124371f5e20b5cdb8e5..0c9b2ab888f948c8586dee0d5c03550205a43c3d 100644
--- a/configure.in
+++ b/configure.in
@@ -4,7 +4,7 @@ dnl Copyright (c) 2007-2008, The Tor Project, Inc.
 dnl See LICENSE for licensing information
 
 AC_INIT
-AM_INIT_AUTOMAKE(tor, 0.2.2.10-alpha)
+AM_INIT_AUTOMAKE(tor, 0.2.2.11-alpha)
 AM_CONFIG_HEADER(orconfig.h)
 
 AC_CANONICAL_HOST
@@ -30,6 +30,8 @@ AC_ARG_ENABLE(static-openssl,
    AS_HELP_STRING(--enable-static-openssl, Link against a static openssl library. Requires --with-openssl-dir))
 AC_ARG_ENABLE(static-libevent,
    AS_HELP_STRING(--enable-static-libevent, Link against a static libevent library. Requires --with-libevent-dir))
+AC_ARG_ENABLE(static-zlib,
+   AS_HELP_STRING(--enable-static-zlib, Link against a static zlib library. Requires --with-zlib-dir))
 
 if test x$enable_buf_freelists != xno; then
   AC_DEFINE(ENABLE_BUF_FREELISTS, 1,
@@ -106,18 +108,6 @@ AC_CHECK_PROG([SED],[sed],[sed],[/bin/false])
 dnl check for asciidoc and a2x
 AC_PATH_PROG([ASCIIDOC], [asciidoc], none)
 AC_PATH_PROG([A2X], [a2x], none)
-AC_PATH_PROG([XSLTPROC], [xsltproc], none)
-if test x$asciidoc = xtrue ; then
-   if test x$ASCIIDOC = xnone ; then
-       AC_MSG_ERROR("Couldn't find asciidoc. reconfigure with --disable-asciidoc to build without asciidoc.")
-   fi
-   if test x$A2X = xnone ; then
-       AC_MSG_ERROR("Couldn't find a2x. reconfigure with --disable-asciidoc to build without a2x.")
-   fi
-   if test x$XSLTPROC = xnone ; then
-       AC_MSG_ERROR("Couldn't find xsltproc. reconfigure with --disable-asciidoc to build without xsltproc.")
-   fi
-fi
 
 AM_CONDITIONAL(USE_ASCIIDOC, test x$asciidoc = xtrue)
 
@@ -212,7 +202,7 @@ dnl -------------------------------------------------------------------
 dnl Check for functions before libevent, since libevent-1.2 apparently
 dnl exports strlcpy without defining it in a header.
 
-AC_CHECK_FUNCS(gettimeofday ftime socketpair uname inet_aton strptime getrlimit strlcat strlcpy strtoull getaddrinfo localtime_r gmtime_r memmem strtok_r writev readv flock prctl)
+AC_CHECK_FUNCS(gettimeofday ftime socketpair uname inet_aton strptime getrlimit strlcat strlcpy strtoull getaddrinfo localtime_r gmtime_r memmem strtok_r writev readv flock prctl vasprintf)
 
 using_custom_malloc=no
 if test x$enable_openbsd_malloc = xyes ; then
@@ -363,6 +353,19 @@ TOR_SEARCH_LIBRARY(zlib, $tryzlibdir, [-lz],
     [zlibVersion(); exit(0);], [--with-zlib-dir],
     [/opt/zlib])
 
+if test "$enable_static_zlib" = "yes"; then
+   if test "$tor_cv_library_zlib_dir" = "(system)"; then
+     AC_MSG_ERROR("You must specify an explicit --with-zlib-dir=x option when
+ using --enable-static-zlib")
+   else
+     TOR_ZLIB_LIBS="$TOR_LIBDIR_zlib/libz.a"
+     echo "$TOR_LIBDIR_zlib/libz.a"
+   fi
+else
+     TOR_ZLIB_LIBS="-lz"
+fi
+AC_SUBST(TOR_ZLIB_LIBS)
+
 dnl Make sure to enable support for large off_t if available.
 
 AC_SYS_LARGEFILE
diff --git a/contrib/tor-exit-notice.html b/contrib/tor-exit-notice.html
index 78a148ccc6ee39dd3e1418f79da7a35a7ad91831..8b37edc3febd235158b5f08b256e3b41b19ffdb0 100644
--- a/contrib/tor-exit-notice.html
+++ b/contrib/tor-exit-notice.html
@@ -29,6 +29,7 @@ They are marked with FIXME.
 Tor Exit Router</p>
 
 <p>
+Most likely you are accessing this website because you had some issue with
 the traffic coming from this IP. This router is part of the <a
 href="https://www.torproject.org/">Tor Anonymity Network</a>, which is
 dedicated to <a href="https://www.torproject.org/overview.html">providing
diff --git a/contrib/tor-mingw.nsi.in b/contrib/tor-mingw.nsi.in
index bc9eef08aff2223168c4514cdb7523593248de83..b041dadf2b76d943b1f310167bc53ab5907a626b 100644
--- a/contrib/tor-mingw.nsi.in
+++ b/contrib/tor-mingw.nsi.in
@@ -8,8 +8,7 @@
 !include "LogicLib.nsh"
 !include "FileFunc.nsh"
 !insertmacro GetParameters
-
-!define VERSION "0.2.2.10-alpha"
+!define VERSION "0.2.2.11-alpha"
 !define INSTALLER "tor-${VERSION}-win32.exe"
 !define WEBSITE "https://www.torproject.org/"
 !define LICENSE "LICENSE"
diff --git a/doc/asciidoc-helper.sh b/doc/asciidoc-helper.sh
index d24b31918c6290752cce08ea4066488d75d9a294..ea0efbefdca313950389ac55eab3688c7d263075 100755
--- a/doc/asciidoc-helper.sh
+++ b/doc/asciidoc-helper.sh
@@ -17,21 +17,43 @@ output=$3
 if [ "$1" = "html" ]; then
     input=${output%%.html.in}.1.txt
     base=${output%%.html.in}
-    "$2" -d manpage -o $output $input;
+    if [ "$2" != none ]; then
+      "$2" -d manpage -o $output $input;
+    else
+      echo "==================================";
+      echo;
+      echo "You need asciidoc installed to be able to build the manpage.";
+      echo "To build without manpages, use the --disable-asciidoc argument";
+      echo "when calling configure.";
+      echo;
+      echo "==================================";
+      exit 1;
+    fi
 elif [ "$1" = "man" ]; then
     input=${output%%.1.in}.1.txt
     base=${output%%.1.in}
     
+    if test "$2" = none; then
+      echo "==================================";
+      echo;
+      echo "You need asciidoc installed to be able to build the manpage.";
+      echo "To build without manpages, use the --disable-asciidoc argument";
+      echo "when calling configure.";
+      echo;
+      echo "==================================";
+      exit 1;
+    fi
     if "$2" -f manpage $input; then
       mv $base.1 $output;
     else
       echo "==================================";
       echo;
       echo "a2x is installed, but some required docbook support files are";
-      echo "missing. Please install docbook-xsl and docbook-xml (Debian)";
-      echo "or similar.";
+      echo "missing. Please install docbook-xsl, docbook-xml, and";
+      echo "libxml2-utils (Debian) or similar.";
       echo;
       echo "==================================";
       exit 1;
     fi
 fi
+
diff --git a/doc/spec/control-spec.txt b/doc/spec/control-spec.txt
index 9ae51b0a1bfb4bcc7b4ee67343e18ed4d566a1b0..b60baba052aacd73a6558bb09f6f2dc73f1c52e1 100644
--- a/doc/spec/control-spec.txt
+++ b/doc/spec/control-spec.txt
@@ -592,7 +592,7 @@
       List of currently recommended versions.
     "status/version/current"
       Status of the current version. One of: new, old, unrecommended,
-      recommended, new in series, obsolete.
+      recommended, new in series, obsolete, unknown.
     "status/clients-seen"
       A summary of which countries we've seen clients from recently,
       formatted the same as the CLIENTS_SEEN status event described in
diff --git a/doc/spec/dir-spec.txt b/doc/spec/dir-spec.txt
index 442e7d5824829264c17bc23497db97a70df3d618..b88e838f3696cc4a8d582dc7541adf92330d43a2 100644
--- a/doc/spec/dir-spec.txt
+++ b/doc/spec/dir-spec.txt
@@ -10,7 +10,7 @@
 
    Caches and authorities must still support older versions of the
    directory protocols, until the versions of Tor that require them are
-   finally out of commission.  See Section XXXX on backward compatibility.
+   finally out of commission.
 
    This document merges and supersedes the following proposals:
 
@@ -182,7 +182,8 @@
    All directory information is uploaded and downloaded with HTTP.
 
    [Authorities also generate and caches also cache documents produced and
-   used by earlier versions of this protocol; see section XXX for notes.]
+   used by earlier versions of this protocol; see dir-spec-v1.txt and
+   dir-spec-v2.txt for notes on those versions.]
 
 1.1. What's different from version 2?
 
@@ -591,7 +592,7 @@
         with unrecognized items; the protocols line should be preceded with
         an "opt" until these Tors are obsolete.]
 
-   "allow-single-hop-exits"
+   "allow-single-hop-exits" NL
 
        [At most once.]
 
@@ -612,7 +613,7 @@
         Fingerprint is encoded in hex (using upper-case letters), with
         no spaces.
 
-    "published"
+    "published" YYYY-MM-DD HH:MM:SS NL
 
        [Exactly once.]
 
@@ -1015,7 +1016,7 @@
    generate exactly the same consensus given the same set of votes.
 
    The procedure for deciding when to generate vote and consensus status
-   documents are described in section XXX below.
+   documents are described in section 1.4 on the voting timeline.
 
    Status documents contain a preamble, an authority section, a list of
    router status entries, and one or more footer signature, in that order.
@@ -1148,9 +1149,11 @@
         transit in the network at any given time. Obeyed by Tor 0.2.1.20
         and later.
 
-        "CircPriorityHalflifeMsec" -- the halflife parameter used when
+        "CircuitPriorityHalflifeMsec" -- the halflife parameter used when
         weighting which circuit will send the next cell. Obeyed by Tor
-        0.2.2.7-alpha and later.
+        0.2.2.10-alpha and later.  (Versions of Tor between 0.2.2.7-alpha
+        and 0.2.2.10-alpha recognized a "CircPriorityHalflifeMsec" parameter,
+        but mishandled it badly.)
 
    The authority section of a vote contains the following items, followed
    in turn by the authority's current key certificate:
@@ -1367,7 +1370,7 @@
         the signing authority, and "signing-key-digest" is the hex-encoded
         digest of the current authority signing key of the signing authority.
 
-3.3. Deciding how to vote.
+3.3. Assigning flags in a vote
 
    (This section describes how directory authorities choose which status
    flags to apply to routers, as of Tor 0.2.0.0-alpha-dev. Later directory
diff --git a/src/common/compat.c b/src/common/compat.c
index 7f53704c6905e089a4fbbf4a6a7e5277d4c33f15..26038c1099a407d9d640a0960c8d2e6b5b633a0d 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -307,6 +307,100 @@ tor_vsnprintf(char *str, size_t size, const char *format, va_list args)
   return r;
 }
 
+/**
+ * Portable asprintf implementation.  Does a printf() into a newly malloc'd
+ * string.  Sets *<b>strp</b> to this string, and returns its length (not
+ * including the terminating NUL character).
+ *
+ * You can treat this function as if its implementation were something like
+   <pre>
+     char buf[_INFINITY_];
+     tor_snprintf(buf, sizeof(buf), fmt, args);
+     *strp = tor_strdup(buf);
+     return strlen(*strp):
+   </pre>
+ * Where _INFINITY_ is an imaginary constant so big that any string can fit
+ * into it.
+ */
+int
+tor_asprintf(char **strp, const char *fmt, ...)
+{
+  int r;
+  va_list args;
+  va_start(args, fmt);
+  r = tor_vasprintf(strp, fmt, args);
+  va_end(args);
+  if (!*strp || r < 0) {
+    log_err(LD_BUG, "Internal error in asprintf");
+    tor_assert(0);
+  }
+  return r;
+}
+
+/**
+ * Portable vasprintf implementation.  Does a printf() into a newly malloc'd
+ * string.  Differs from regular vasprintf in the same ways that
+ * tor_asprintf() differs from regular asprintf.
+ */
+int
+tor_vasprintf(char **strp, const char *fmt, va_list args)
+{
+  /* use a temporary variable in case *strp is in args. */
+  char *strp_tmp=NULL;
+#ifdef HAVE_VASPRINTF
+  /* If the platform gives us one, use it. */
+  int r = vasprintf(&strp_tmp, fmt, args);
+  if (r < 0)
+    *strp = NULL;
+  else
+    *strp = strp_tmp;
+  return r;
+#elif defined(MS_WINDOWS)
+  /* On Windows, _vsnprintf won't tell us the length of the string if it
+   * overflows, so we need to use _vcsprintf to tell how much to allocate */
+  int len, r;
+  char *res;
+  len = _vcsprintf(fmt, args);
+  if (len < 0) {
+    *strp = NULL;
+    return -1;
+  }
+  strp_tmp = tor_malloc(len + 1);
+  r = _vsnprintf(strp_tmp, len+1, fmt, args);
+  if (r != len) {
+    tor_free(strp_tmp);
+    *strp = NULL;
+    return -1;
+  }
+  *strp = strp_tmp;
+  return len;
+#else
+  /* Everywhere else, we have a decent vsnprintf that tells us how many
+   * characters we need.  We give it a try on a short buffer first, since
+   * it might be nice to avoid the second vsnprintf call.
+   */
+  char buf[128];
+  int len, r;
+  va_list tmp_args;
+  va_copy(tmp_args, args);
+  len = vsnprintf(buf, sizeof(buf), fmt, tmp_args);
+  va_end(tmp_args);
+  if (len < (int)sizeof(buf)) {
+    *strp = tor_strdup(buf);
+    return len;
+  }
+  strp_tmp = tor_malloc(len+1);
+  r = vsnprintf(strp_tmp, len+1, fmt, args);
+  if (r != len) {
+    tor_free(strp_tmp);
+    *strp = NULL;
+    return -1;
+  }
+  *strp = strp_tmp;
+  return len;
+#endif
+}
+
 /** Given <b>hlen</b> bytes at <b>haystack</b> and <b>nlen</b> bytes at
  * <b>needle</b>, return a pointer to the first occurrence of the needle
  * within the haystack, or NULL if there is no such occurrence.
diff --git a/src/common/compat.h b/src/common/compat.h
index f5f8bb4283d5d0bc904964180715b8e7061d8cb5..dbadd605092100b44cf26aa10a7eb4f8f671fe40 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -243,6 +243,10 @@ int tor_snprintf(char *str, size_t size, const char *format, ...)
 int tor_vsnprintf(char *str, size_t size, const char *format, va_list args)
   ATTR_NONNULL((1,3));
 
+int tor_asprintf(char **strp, const char *fmt, ...)
+  CHECK_PRINTF(2,3);
+int tor_vasprintf(char **strp, const char *fmt, va_list args);
+
 const void *tor_memmem(const void *haystack, size_t hlen, const void *needle,
                        size_t nlen)  ATTR_PURE ATTR_NONNULL((1,3));
 static const void *tor_memstr(const void *haystack, size_t hlen,
diff --git a/src/common/tortls.c b/src/common/tortls.c
index b4984802fb5607735271c6f0411fc232432d264d..df77fb066f897d9e04d07bb6bde5a4c0ac7b8d59 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -368,8 +368,8 @@ tor_tls_init(void)
      * OpenSSL 0.9.8l.
      *
      * No, we can't just set flag 0x0010 everywhere.  It breaks Tor with
-     * OpenSSL 1.0.0beta3 and later.  No, we can't just set option
-     * 0x00040000L everywhere: before 0.9.8m, it meant something else.
+     * OpenSSL 1.0.0beta3 and later.  On the other hand, we might be able to
+     * set option 0x00040000L everywhere.
      *
      * No, we can't simply detect whether the flag or the option is present
      * in the headers at build-time: some vendors (notably Apple) like to
@@ -393,10 +393,12 @@ tor_tls_init(void)
     } else if (version < 0x009080c0L) {
       log_notice(LD_GENERAL, "OpenSSL %s [%lx] looks like it's older than "
                  "0.9.8l, but some vendors have backported 0.9.8l's "
-                 "renegotiation code to earlier versions.  I'll set "
-                 "SSL3_FLAGS just to be safe.",
+                 "renegotiation code to earlier versions, and some have "
+                 "backported the code from 0.9.8m or 0.9.8n.  I'll set both "
+                 "SSL3_FLAGS and SSL_OP just to be safe.",
                  SSLeay_version(SSLEAY_VERSION), version);
       use_unsafe_renegotiation_flag = 1;
+      use_unsafe_renegotiation_op = 1;
     } else {
       log_info(LD_GENERAL, "OpenSSL %s has version %lx",
                SSLeay_version(SSLEAY_VERSION), version);
diff --git a/src/or/Makefile.am b/src/or/Makefile.am
index cfa8a035ad9ee6fd6f0cf926658bd4a91940210d..a354db5db316a4e7c253d05b691f7131b8fb6619 100644
--- a/src/or/Makefile.am
+++ b/src/or/Makefile.am
@@ -43,7 +43,7 @@ AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
 tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
 tor_LDADD = ./libtor.a ../common/libor.a ../common/libor-crypto.a \
 	../common/libor-event.a \
-	-lz -lm @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
+	@TOR_ZLIB_LIBS@ -lm @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
 
 noinst_HEADERS = or.h eventdns.h eventdns_tor.h micro-revision.i
 
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index cd5ada8dce4bd7af0f20a9abf3dd3cb86d7a8388..233d60f15c1f5456331885592b586c86abf5fd7d 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -1085,21 +1085,21 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names)
   crypt_path_t *hop;
   smartlist_t *elements;
   const char *states[] = {"closed", "waiting for keys", "open"};
-  char buf[128];
   char *s;
 
   elements = smartlist_create();
 
   if (verbose) {
     const char *nickname = build_state_get_exit_nickname(circ->build_state);
-    tor_snprintf(buf, sizeof(buf), "%s%s circ (length %d%s%s):",
+    char *cp;
+    tor_asprintf(&cp, "%s%s circ (length %d%s%s):",
                  circ->build_state->is_internal ? "internal" : "exit",
                  circ->build_state->need_uptime ? " (high-uptime)" : "",
                  circ->build_state->desired_path_len,
                  circ->_base.state == CIRCUIT_STATE_OPEN ? "" : ", exit ",
                  circ->_base.state == CIRCUIT_STATE_OPEN ? "" :
                  (nickname?nickname:"*unnamed*"));
-    smartlist_add(elements, tor_strdup(buf));
+    smartlist_add(elements, cp);
   }
 
   hop = circ->cpath;
@@ -3068,21 +3068,21 @@ static void
 log_entry_guards(int severity)
 {
   smartlist_t *elements = smartlist_create();
-  char buf[1024];
   char *s;
 
   SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e,
     {
       const char *msg = NULL;
+      char *cp;
       if (entry_is_live(e, 0, 1, 0, &msg))
-        tor_snprintf(buf, sizeof(buf), "%s (up %s)",
+        tor_asprintf(&cp, "%s (up %s)",
                      e->nickname,
                      e->made_contact ? "made-contact" : "never-contacted");
       else
-        tor_snprintf(buf, sizeof(buf), "%s (%s, %s)",
+        tor_asprintf(&cp, "%s (%s, %s)",
                      e->nickname, msg,
                      e->made_contact ? "made-contact" : "never-contacted");
-      smartlist_add(elements, tor_strdup(buf));
+      smartlist_add(elements, cp);
     });
 
   s = smartlist_join_strings(elements, ",", 0, NULL);
diff --git a/src/or/config.c b/src/or/config.c
index cbf9a5a0c691af5f262a082dd120046d6d2543a7..5d07cd7343aaf63e0b54700648fcc334bcfd528d 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -299,6 +299,7 @@ static config_var_t _option_vars[] = {
   V(RecommendedClientVersions,   LINELIST, NULL),
   V(RecommendedServerVersions,   LINELIST, NULL),
   OBSOLETE("RedirectExit"),
+  V(RefuseUnknownExits,          BOOL,     "0"),
   V(RejectPlaintextPorts,        CSV,      ""),
   V(RelayBandwidthBurst,         MEMUNIT,  "0"),
   V(RelayBandwidthRate,          MEMUNIT,  "0"),
@@ -956,11 +957,9 @@ options_act_reversible(or_options_t *old_options, char **msg)
   /* Ensure data directory is private; create if possible. */
   if (check_private_dir(options->DataDirectory,
                         running_tor ? CPD_CREATE : CPD_CHECK)<0) {
-    char buf[1024];
-    int tmp = tor_snprintf(buf, sizeof(buf),
+    tor_asprintf(msg,
               "Couldn't access/create private data directory \"%s\"",
               options->DataDirectory);
-    *msg = tor_strdup(tmp >= 0 ? buf : "internal error");
     goto done;
     /* No need to roll back, since you can't change the value. */
   }
@@ -971,10 +970,8 @@ options_act_reversible(or_options_t *old_options, char **msg)
     tor_snprintf(fn, len, "%s"PATH_SEPARATOR"cached-status",
                  options->DataDirectory);
     if (check_private_dir(fn, running_tor ? CPD_CREATE : CPD_CHECK) < 0) {
-      char buf[1024];
-      int tmp = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
                 "Couldn't access/create private data directory \"%s\"", fn);
-      *msg = tor_strdup(tmp >= 0 ? buf : "internal error");
       tor_free(fn);
       goto done;
     }
@@ -1545,8 +1542,7 @@ static int
 config_assign_value(config_format_t *fmt, or_options_t *options,
                     config_line_t *c, char **msg)
 {
-  int i, r, ok;
-  char buf[1024];
+  int i, ok;
   config_var_t *var;
   void *lvalue;
 
@@ -1562,10 +1558,9 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
   case CONFIG_TYPE_UINT:
     i = (int)tor_parse_long(c->value, 10, 0, INT_MAX, &ok, NULL);
     if (!ok) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
           "Int keyword '%s %s' is malformed or out of bounds.",
           c->key, c->value);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
     *(int *)lvalue = i;
@@ -1574,10 +1569,9 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
   case CONFIG_TYPE_INTERVAL: {
     i = config_parse_interval(c->value, &ok);
     if (!ok) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
           "Interval '%s %s' is malformed or out of bounds.",
           c->key, c->value);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
     *(int *)lvalue = i;
@@ -1587,10 +1581,9 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
   case CONFIG_TYPE_MEMUNIT: {
     uint64_t u64 = config_parse_memunit(c->value, &ok);
     if (!ok) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
           "Value '%s %s' is malformed or out of bounds.",
           c->key, c->value);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
     *(uint64_t *)lvalue = u64;
@@ -1600,10 +1593,9 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
   case CONFIG_TYPE_BOOL:
     i = (int)tor_parse_long(c->value, 10, 0, 1, &ok, NULL);
     if (!ok) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
           "Boolean '%s %s' expects 0 or 1.",
           c->key, c->value);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
     *(int *)lvalue = i;
@@ -1621,9 +1613,8 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
 
   case CONFIG_TYPE_ISOTIME:
     if (parse_iso_time(c->value, (time_t *)lvalue)) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
           "Invalid time '%s' for keyword '%s'", c->value, c->key);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
     break;
@@ -1634,9 +1625,8 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
     }
     *(routerset_t**)lvalue = routerset_new();
     if (routerset_parse(*(routerset_t**)lvalue, c->value, c->key)<0) {
-      tor_snprintf(buf, sizeof(buf), "Invalid exit list '%s' for option '%s'",
+      tor_asprintf(msg, "Invalid exit list '%s' for option '%s'",
                    c->value, c->key);
-      *msg = tor_strdup(buf);
       return -1;
     }
     break;
@@ -1661,9 +1651,8 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
     log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key);
     break;
   case CONFIG_TYPE_LINELIST_V:
-    r = tor_snprintf(buf, sizeof(buf),
+    tor_asprintf(msg,
         "You may not provide a value for virtual option '%s'", c->key);
-    *msg = tor_strdup(r >= 0 ? buf : "internal error");
     return -1;
   default:
     tor_assert(0);
@@ -1699,10 +1688,8 @@ config_assign_line(config_format_t *fmt, or_options_t *options,
       config_line_append((config_line_t**)lvalue, c->key, c->value);
       return 0;
     } else {
-      char buf[1024];
-      int tmp = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
                 "Unknown option '%s'.  Failing.", c->key);
-      *msg = tor_strdup(tmp >= 0 ? buf : "internal error");
       return -1;
     }
   }
@@ -1828,7 +1815,6 @@ get_assigned_option(config_format_t *fmt, void *options,
 {
   config_var_t *var;
   const void *value;
-  char buf[32];
   config_line_t *result;
   tor_assert(options && key);
 
@@ -1869,19 +1855,16 @@ get_assigned_option(config_format_t *fmt, void *options,
     case CONFIG_TYPE_UINT:
       /* This means every or_options_t uint or bool element
        * needs to be an int. Not, say, a uint16_t or char. */
-      tor_snprintf(buf, sizeof(buf), "%d", *(int*)value);
-      result->value = tor_strdup(buf);
+      tor_asprintf(&result->value, "%d", *(int*)value);
       escape_val = 0; /* Can't need escape. */
       break;
     case CONFIG_TYPE_MEMUNIT:
-      tor_snprintf(buf, sizeof(buf), U64_FORMAT,
+      tor_asprintf(&result->value, U64_FORMAT,
                    U64_PRINTF_ARG(*(uint64_t*)value));
-      result->value = tor_strdup(buf);
       escape_val = 0; /* Can't need escape. */
       break;
     case CONFIG_TYPE_DOUBLE:
-      tor_snprintf(buf, sizeof(buf), "%f", *(double*)value);
-      result->value = tor_strdup(buf);
+      tor_asprintf(&result->value, "%f", *(double*)value);
       escape_val = 0; /* Can't need escape. */
       break;
     case CONFIG_TYPE_BOOL:
@@ -2604,15 +2587,10 @@ config_dump(config_format_t *fmt, void *options, int minimal,
     line = assigned = get_assigned_option(fmt, options, fmt->vars[i].name, 1);
 
     for (; line; line = line->next) {
-      size_t len = strlen(line->key) + strlen(line->value) + 5;
       char *tmp;
-      tmp = tor_malloc(len);
-      if (tor_snprintf(tmp, len, "%s%s %s\n",
-                       comment_option ? "# " : "",
-                       line->key, line->value)<0) {
-        log_err(LD_BUG,"Internal error writing option value");
-        tor_assert(0);
-      }
+      tor_asprintf(&tmp, "%s%s %s\n",
+                   comment_option ? "# " : "",
+                   line->key, line->value);
       smartlist_add(elements, tmp);
     }
     config_free_lines(assigned);
@@ -2621,13 +2599,8 @@ config_dump(config_format_t *fmt, void *options, int minimal,
   if (fmt->extra) {
     line = *(config_line_t**)STRUCT_VAR_P(options, fmt->extra->var_offset);
     for (; line; line = line->next) {
-      size_t len = strlen(line->key) + strlen(line->value) + 3;
       char *tmp;
-      tmp = tor_malloc(len);
-      if (tor_snprintf(tmp, len, "%s %s\n", line->key, line->value)<0) {
-        log_err(LD_BUG,"Internal error writing option value");
-        tor_assert(0);
-      }
+      tor_asprintf(&tmp, "%s %s\n", line->key, line->value);
       smartlist_add(elements, tmp);
     }
   }
@@ -2656,7 +2629,6 @@ static int
 validate_ports_csv(smartlist_t *sl, const char *name, char **msg)
 {
   int i;
-  char buf[1024];
   tor_assert(name);
 
   if (!sl)
@@ -2666,9 +2638,7 @@ validate_ports_csv(smartlist_t *sl, const char *name, char **msg)
   {
     i = atoi(cp);
     if (i < 1 || i > 65535) {
-      int r = tor_snprintf(buf, sizeof(buf),
-                           "Port '%s' out of range in %s", cp, name);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
+      tor_asprintf(msg, "Port '%s' out of range in %s", cp, name);
       return -1;
     }
   });
@@ -2682,18 +2652,15 @@ validate_ports_csv(smartlist_t *sl, const char *name, char **msg)
 static int
 ensure_bandwidth_cap(uint64_t *value, const char *desc, char **msg)
 {
-  int r;
-  char buf[1024];
   if (*value > ROUTER_MAX_DECLARED_BANDWIDTH) {
     /* This handles an understandable special case where somebody says "2gb"
      * whereas our actual maximum is 2gb-1 (INT_MAX) */
     --*value;
   }
   if (*value > ROUTER_MAX_DECLARED_BANDWIDTH) {
-    r = tor_snprintf(buf, sizeof(buf), "%s ("U64_FORMAT") must be at most %d",
-                     desc, U64_PRINTF_ARG(*value),
-                     ROUTER_MAX_DECLARED_BANDWIDTH);
-    *msg = tor_strdup(r >= 0 ? buf : "internal error");
+    tor_asprintf(msg, "%s ("U64_FORMAT") must be at most %d",
+                 desc, U64_PRINTF_ARG(*value),
+                 ROUTER_MAX_DECLARED_BANDWIDTH);
     return -1;
   }
   return 0;
@@ -2768,10 +2735,9 @@ static int
 options_validate(or_options_t *old_options, or_options_t *options,
                  int from_setconf, char **msg)
 {
-  int i, r;
+  int i;
   config_line_t *cl;
   const char *uname = get_uname();
-  char buf[1024];
 #define REJECT(arg) \
   STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
 #define COMPLAIN(arg) STMT_BEGIN log(LOG_WARN, LD_CONFIG, arg); STMT_END
@@ -2866,10 +2832,9 @@ options_validate(or_options_t *old_options, or_options_t *options,
     }
   } else {
     if (!is_legal_nickname(options->Nickname)) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
           "Nickname '%s' is wrong length or contains illegal characters.",
           options->Nickname);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
   }
@@ -3018,10 +2983,9 @@ options_validate(or_options_t *old_options, or_options_t *options,
            "FetchDirInfoEarly");
 
   if (options->ConnLimit <= 0) {
-    r = tor_snprintf(buf, sizeof(buf),
+    tor_asprintf(msg,
         "ConnLimit must be greater than 0, but was set to %d",
         options->ConnLimit);
-    *msg = tor_strdup(r >= 0 ? buf : "internal error");
     return -1;
   }
 
@@ -3140,9 +3104,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
         else if (!strcasecmp(cp, "rendezvous"))
           options->_AllowInvalid |= ALLOW_INVALID_RENDEZVOUS;
         else {
-          r = tor_snprintf(buf, sizeof(buf),
+          tor_asprintf(msg,
               "Unrecognized value '%s' in AllowInvalidNodes", cp);
-          *msg = tor_strdup(r >= 0 ? buf : "internal error");
           return -1;
         }
       });
@@ -3156,17 +3119,14 @@ options_validate(or_options_t *old_options, or_options_t *options,
   } else if (!strcasecmp(options->SafeLogging, "1")) {
     options->_SafeLogging = SAFELOG_SCRUB_ALL;
   } else {
-    r = tor_snprintf(buf, sizeof(buf),
+    tor_asprintf(msg,
                      "Unrecognized value '%s' in SafeLogging",
                      escaped(options->SafeLogging));
-    *msg = tor_strdup(r >= 0 ? buf : "internal error");
     return -1;
   }
 
   if (compute_publishserverdescriptor(options) < 0) {
-    r = tor_snprintf(buf, sizeof(buf),
-                     "Unrecognized value in PublishServerDescriptor");
-    *msg = tor_strdup(r >= 0 ? buf : "internal error");
+    tor_asprintf(msg, "Unrecognized value in PublishServerDescriptor");
     return -1;
   }
 
@@ -3237,31 +3197,28 @@ options_validate(or_options_t *old_options, or_options_t *options,
 
   if (server_mode(options)) {
     if (options->BandwidthRate < ROUTER_REQUIRED_MIN_BANDWIDTH) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
                        "BandwidthRate is set to %d bytes/second. "
                        "For servers, it must be at least %d.",
                        (int)options->BandwidthRate,
                        ROUTER_REQUIRED_MIN_BANDWIDTH);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     } else if (options->MaxAdvertisedBandwidth <
                ROUTER_REQUIRED_MIN_BANDWIDTH/2) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
                        "MaxAdvertisedBandwidth is set to %d bytes/second. "
                        "For servers, it must be at least %d.",
                        (int)options->MaxAdvertisedBandwidth,
                        ROUTER_REQUIRED_MIN_BANDWIDTH/2);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
     if (options->RelayBandwidthRate &&
       options->RelayBandwidthRate < ROUTER_REQUIRED_MIN_BANDWIDTH) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
                        "RelayBandwidthRate is set to %d bytes/second. "
                        "For servers, it must be at least %d.",
                        (int)options->RelayBandwidthRate,
                        ROUTER_REQUIRED_MIN_BANDWIDTH);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
   }
@@ -3449,11 +3406,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
     if (options->ConstrainedSockSize < MIN_CONSTRAINED_TCP_BUFFER ||
         options->ConstrainedSockSize > MAX_CONSTRAINED_TCP_BUFFER ||
         options->ConstrainedSockSize % 1024) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
           "ConstrainedSockSize is invalid.  Must be a value between %d and %d "
           "in 1024 byte increments.",
           MIN_CONSTRAINED_TCP_BUFFER, MAX_CONSTRAINED_TCP_BUFFER);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
     if (options->DirPort) {
@@ -3629,12 +3585,10 @@ options_transition_allowed(or_options_t *old, or_options_t *new_val,
   }
 
   if (strcmp(old->DataDirectory,new_val->DataDirectory)!=0) {
-    char buf[1024];
-    int r = tor_snprintf(buf, sizeof(buf),
+    tor_asprintf(msg,
                "While Tor is running, changing DataDirectory "
                "(\"%s\"->\"%s\") is not allowed.",
                old->DataDirectory, new_val->DataDirectory);
-    *msg = tor_strdup(r >= 0 ? buf : "internal error");
     return -1;
   }
 
@@ -3818,10 +3772,7 @@ check_nickname_list(const char *lst, const char *name, char **msg)
   SMARTLIST_FOREACH(sl, const char *, s,
     {
       if (!is_legal_nickname_or_hexdigest(s)) {
-        char buf[1024];
-        int tmp = tor_snprintf(buf, sizeof(buf),
-                  "Invalid nickname '%s' in %s line", s, name);
-        *msg = tor_strdup(tmp >= 0 ? buf : "internal error");
+        tor_asprintf(msg, "Invalid nickname '%s' in %s line", s, name);
         r = -1;
         break;
       }
@@ -4124,12 +4075,9 @@ options_init_from_string(const char *cf,
  err:
   config_free(&options_format, newoptions);
   if (*msg) {
-    int len = (int)strlen(*msg)+256;
-    char *newmsg = tor_malloc(len);
-
-    tor_snprintf(newmsg, len, "Failed to parse/validate config: %s", *msg);
-    tor_free(*msg);
-    *msg = newmsg;
+    char *old_msg = *msg;
+    tor_asprintf(msg, "Failed to parse/validate config: %s", old_msg);
+    tor_free(old_msg);
   }
   return err;
 }
@@ -4549,7 +4497,6 @@ write_configuration_file(const char *fname, or_options_t *options)
 {
   char *old_val=NULL, *new_val=NULL, *new_conf=NULL;
   int rename_old = 0, r;
-  size_t len;
 
   tor_assert(fname);
 
@@ -4576,9 +4523,7 @@ write_configuration_file(const char *fname, or_options_t *options)
     goto err;
   }
 
-  len = strlen(new_conf)+256;
-  new_val = tor_malloc(len);
-  tor_snprintf(new_val, len, "%s\n%s\n\n%s",
+  tor_asprintf(&new_val, "%s\n%s\n\n%s",
                GENERATED_FILE_PREFIX, GENERATED_FILE_COMMENT, new_conf);
 
   if (rename_old) {
@@ -5022,7 +4967,6 @@ or_state_save(time_t now)
 {
   char *state, *contents;
   char tbuf[ISO_TIME_LEN+1];
-  size_t len;
   char *fname;
 
   tor_assert(global_state);
@@ -5040,15 +4984,11 @@ or_state_save(time_t now)
 
   global_state->LastWritten = time(NULL);
   tor_free(global_state->TorVersion);
-  len = strlen(get_version())+8;
-  global_state->TorVersion = tor_malloc(len);
-  tor_snprintf(global_state->TorVersion, len, "Tor %s", get_version());
+  tor_asprintf(&global_state->TorVersion, "Tor %s", get_version());
 
   state = config_dump(&state_format, global_state, 1, 0);
-  len = strlen(state)+256;
-  contents = tor_malloc(len);
   format_local_iso_time(tbuf, time(NULL));
-  tor_snprintf(contents, len,
+  tor_asprintf(&contents,
                "# Tor state file last generated on %s local time\n"
                "# Other times below are in GMT\n"
                "# You *do not* need to edit this file.\n\n%s",
@@ -5102,7 +5042,6 @@ getinfo_helper_config(control_connection_t *conn,
       config_var_t *var = &_option_vars[i];
       const char *type;
       char *line;
-      size_t len;
       switch (var->type) {
         case CONFIG_TYPE_STRING: type = "String"; break;
         case CONFIG_TYPE_FILENAME: type = "Filename"; break;
@@ -5123,9 +5062,7 @@ getinfo_helper_config(control_connection_t *conn,
       }
       if (!type)
         continue;
-      len = strlen(var->name)+strlen(type)+16;
-      line = tor_malloc(len);
-      tor_snprintf(line, len, "%s %s\n",var->name,type);
+      tor_asprintf(&line, "%s %s\n",var->name,type);
       smartlist_add(sl, line);
     }
     *answer = smartlist_join_strings(sl, "", 0, NULL);
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 861482d2af1af93a1c971d7ee441543913646673..a173dc1226d415b996cf6fac8ce264b31d515574 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -2505,16 +2505,28 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
       tor_free(address);
       return 0;
     }
-    if (or_circ && or_circ->is_first_hop &&
-        !get_options()->AllowSingleHopExits) {
+    if (or_circ && or_circ->p_conn && !get_options()->AllowSingleHopExits &&
+        (or_circ->is_first_hop ||
+         (!connection_or_digest_is_known_relay(
+                                       or_circ->p_conn->identity_digest) &&
+//        XXX022 commented out so we can test it first in 0.2.2.11 -RD
+//        networkstatus_get_param(NULL, "refuseunknownexits", 1)))) {
+          get_options()->RefuseUnknownExits))) {
       /* Don't let clients use us as a single-hop proxy, unless the user
-       * has explicitly allowed that in the config.  It attracts attackers
+       * has explicitly allowed that in the config. It attracts attackers
        * and users who'd be better off with, well, single-hop proxies.
        */
-      log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
-             "Attempt to open a stream on first hop of circuit. Closing.");
+//    log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+      log_notice(LD_PROTOCOL,
+             "Attempt by %s to open a stream %s. Closing.",
+             safe_str(or_circ->p_conn->_base.address),
+             or_circ->is_first_hop ? "on first hop of circuit" :
+                                     "from unknown relay");
       relay_send_end_cell_from_edge(rh.stream_id, circ,
-                                    END_STREAM_REASON_TORPROTOCOL, NULL);
+                                    or_circ->is_first_hop ?
+                                      END_STREAM_REASON_TORPROTOCOL :
+                                      END_STREAM_REASON_MISC,
+                                    NULL);
       tor_free(address);
       return 0;
     }
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index dfd0a965b067aa144312ad2b398156de2f8ba8bb..98525f16a206aff4c5d369270de0f64411116602 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -322,7 +322,7 @@ connection_or_finished_connecting(or_connection_t *or_conn)
 
 /** Return 1 if identity digest <b>id_digest</b> is known to be a
  * currently or recently running relay. Otherwise return 0. */
-static int
+int
 connection_or_digest_is_known_relay(const char *id_digest)
 {
   if (router_get_consensus_status_by_id(id_digest))
diff --git a/src/or/control.c b/src/or/control.c
index 771beaeb58f2da0412ec4670ba88170c5fb1086e..5797edfdcef599580c5d5b828d7fb1e4b1c946c2 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -1883,18 +1883,18 @@ static char *
 list_getinfo_options(void)
 {
   int i;
-  char buf[300];
+  char *buf=NULL;
   smartlist_t *lines = smartlist_create();
   char *ans;
   for (i = 0; getinfo_items[i].varname; ++i) {
     if (!getinfo_items[i].desc)
       continue;
 
-    tor_snprintf(buf, sizeof(buf), "%s%s -- %s\n",
+    tor_asprintf(&buf, "%s%s -- %s\n",
                  getinfo_items[i].varname,
                  getinfo_items[i].is_prefix ? "*" : "",
                  getinfo_items[i].desc);
-    smartlist_add(lines, tor_strdup(buf));
+    smartlist_add(lines, buf);
   }
   smartlist_sort_strings(lines);
 
diff --git a/src/or/directory.c b/src/or/directory.c
index 39e67c957fbfa1c4a8ddec8f44084ef352f5a588..52e1c392cf684c3fd45c3f7d28ada2d0279245c5 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -130,6 +130,8 @@ authority_type_to_string(authority_type_t auth)
     smartlist_add(lst, (void*)"V1");
   if (auth & V2_AUTHORITY)
     smartlist_add(lst, (void*)"V2");
+  if (auth & V3_AUTHORITY)
+    smartlist_add(lst, (void*)"V3");
   if (auth & BRIDGE_AUTHORITY)
     smartlist_add(lst, (void*)"Bridge");
   if (auth & HIDSERV_AUTHORITY)
@@ -311,12 +313,14 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
     case DIR_PURPOSE_FETCH_EXTRAINFO:
       type = EXTRAINFO_CACHE |
              (router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_AUTHORITY :
-                                                        V2_AUTHORITY);
+                                                        V3_AUTHORITY);
       break;
     case DIR_PURPOSE_FETCH_V2_NETWORKSTATUS:
+      type = V2_AUTHORITY;
+      break;
     case DIR_PURPOSE_FETCH_SERVERDESC:
       type = (router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_AUTHORITY :
-                                                        V2_AUTHORITY);
+                                                        V3_AUTHORITY);
       break;
     case DIR_PURPOSE_FETCH_RENDDESC:
       type = HIDSERV_AUTHORITY;
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 5f1cb85431a525f1e74f47c1eb3a5783dbac4d97..b5c4c7b506e39f1f60fbad11948e0e1ad8442de1 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -1122,12 +1122,12 @@ directory_fetches_from_authorities(or_options_t *options)
     return 0;
   if (server_mode(options) && router_pick_published_address(options, &addr)<0)
     return 1; /* we don't know our IP address; ask an authority. */
-  if (options->DirPort == 0)
+  if (options->DirPort == 0 && !options->RefuseUnknownExits)
     return 0;
   if (!server_mode(options) || !advertised_server_mode())
     return 0;
   me = router_get_my_routerinfo();
-  if (!me || !me->dir_port)
+  if (!me || (!me->dir_port && !options->RefuseUnknownExits))
     return 0; /* if dirport not advertised, return 0 too */
   return 1;
 }
@@ -1167,7 +1167,11 @@ directory_caches_v2_dir_info(or_options_t *options)
 int
 directory_caches_dir_info(or_options_t *options)
 {
-  return options->BridgeRelay != 0 || options->DirPort != 0;
+  if (options->BridgeRelay || options->DirPort)
+    return 1;
+  if (!server_mode(options) || !advertised_server_mode())
+    return 0;
+  return options->RefuseUnknownExits;
 }
 
 /** Return 1 if we want to allow remote people to ask us directory
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index ecf236e8fe80e7205b64d60ac6a502898f4d091e..bae222a191160b7593200a0ddb0860561b52187e 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -1187,7 +1187,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
   chunks = smartlist_create();
 
   {
-    char buf[1024];
+    char *buf=NULL;
     char va_buf[ISO_TIME_LEN+1], fu_buf[ISO_TIME_LEN+1],
       vu_buf[ISO_TIME_LEN+1];
     char *flaglist;
@@ -1196,20 +1196,20 @@ networkstatus_compute_consensus(smartlist_t *votes,
     format_iso_time(vu_buf, valid_until);
     flaglist = smartlist_join_strings(flags, " ", 0, NULL);
 
-    tor_snprintf(buf, sizeof(buf), "network-status-version 3%s%s\n"
+    tor_asprintf(&buf, "network-status-version 3%s%s\n"
                  "vote-status consensus\n",
                  flavor == FLAV_NS ? "" : " ",
                  flavor == FLAV_NS ? "" : flavor_name);
 
-    smartlist_add(chunks, tor_strdup(buf));
+    smartlist_add(chunks, buf);
 
     if (consensus_method >= 2) {
-      tor_snprintf(buf, sizeof(buf), "consensus-method %d\n",
+      tor_asprintf(&buf, "consensus-method %d\n",
                    consensus_method);
-      smartlist_add(chunks, tor_strdup(buf));
+      smartlist_add(chunks, buf);
     }
 
-    tor_snprintf(buf, sizeof(buf),
+    tor_asprintf(&buf,
                  "valid-after %s\n"
                  "fresh-until %s\n"
                  "valid-until %s\n"
@@ -1220,7 +1220,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
                  va_buf, fu_buf, vu_buf,
                  vote_seconds, dist_seconds,
                  client_versions, server_versions, flaglist);
-    smartlist_add(chunks, tor_strdup(buf));
+    smartlist_add(chunks, buf);
 
     tor_free(flaglist);
   }
@@ -1256,15 +1256,14 @@ networkstatus_compute_consensus(smartlist_t *votes,
     } SMARTLIST_FOREACH_END(v);
     smartlist_sort(dir_sources, _compare_dir_src_ents_by_authority_id);
 
-    SMARTLIST_FOREACH(dir_sources, const dir_src_ent_t *, e,
-    {
-      char buf[1024];
+    SMARTLIST_FOREACH_BEGIN(dir_sources, const dir_src_ent_t *, e) {
       struct in_addr in;
       char ip[INET_NTOA_BUF_LEN];
       char fingerprint[HEX_DIGEST_LEN+1];
       char votedigest[HEX_DIGEST_LEN+1];
       networkstatus_t *v = e->v;
       networkstatus_voter_info_t *voter = get_voter(v);
+      char *buf = NULL;
 
       if (e->is_legacy)
         tor_assert(consensus_method >= 2);
@@ -1275,22 +1274,22 @@ networkstatus_compute_consensus(smartlist_t *votes,
       base16_encode(votedigest, sizeof(votedigest), voter->vote_digest,
                     DIGEST_LEN);
 
-      tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(&buf,
                    "dir-source %s%s %s %s %s %d %d\n",
                    voter->nickname, e->is_legacy ? "-legacy" : "",
                    fingerprint, voter->address, ip,
                    voter->dir_port,
                    voter->or_port);
-      smartlist_add(chunks, tor_strdup(buf));
+      smartlist_add(chunks, buf);
       if (! e->is_legacy) {
-        tor_snprintf(buf, sizeof(buf),
+        tor_asprintf(&buf,
                      "contact %s\n"
                      "vote-digest %s\n",
                      voter->contact,
                      votedigest);
-        smartlist_add(chunks, tor_strdup(buf));
+        smartlist_add(chunks, buf);
       }
-    });
+    } SMARTLIST_FOREACH_END(e);
     SMARTLIST_FOREACH(dir_sources, dir_src_ent_t *, e, tor_free(e));
     smartlist_free(dir_sources);
   }
@@ -1426,7 +1425,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
       int naming_conflict = 0;
       int n_listing = 0;
       int i;
-      char buf[256];
+      char *buf=NULL;
       char microdesc_digest[DIGEST256_LEN];
 
       /* Of the next-to-be-considered digest in each voter, which is first? */
@@ -1675,19 +1674,20 @@ networkstatus_compute_consensus(smartlist_t *votes,
         }
       }
 
-      /* Okay!! Now we can write the descriptor... */
-      /*     First line goes into "buf". */
-      routerstatus_format_entry(buf, sizeof(buf), &rs_out, NULL,
-                                rs_format);
-      smartlist_add(chunks, tor_strdup(buf));
+      {
+        char buf[4096];
+        /* Okay!! Now we can write the descriptor... */
+        /*     First line goes into "buf". */
+        routerstatus_format_entry(buf, sizeof(buf), &rs_out, NULL,
+                                  rs_format);
+        smartlist_add(chunks, tor_strdup(buf));
+      }
       /*     Now an m line, if applicable. */
       if (flavor == FLAV_MICRODESC &&
           !tor_digest256_is_zero(microdesc_digest)) {
         char m[BASE64_DIGEST256_LEN+1], *cp;
-        const size_t mlen = BASE64_DIGEST256_LEN+5;
         digest256_to_base64(m, microdesc_digest);
-        cp = tor_malloc(mlen);
-        tor_snprintf(cp, mlen, "m %s\n", m);
+        tor_asprintf(&cp, "m %s\n", m);
         smartlist_add(chunks, cp);
       }
       /*     Next line is all flags.  The "\n" is missing. */
@@ -1701,26 +1701,16 @@ networkstatus_compute_consensus(smartlist_t *votes,
       smartlist_add(chunks, tor_strdup("\n"));
       /*     Now the weight line. */
       if (rs_out.has_bandwidth) {
-        int r = tor_snprintf(buf, sizeof(buf),
-                             "w Bandwidth=%d\n", rs_out.bandwidth);
-        if (r<0) {
-          log_warn(LD_BUG, "Not enough space in buffer for weight line.");
-          *buf = '\0';
-        }
-
-        smartlist_add(chunks, tor_strdup(buf));
-      };
+        char *cp=NULL;
+        tor_asprintf(&cp, "w Bandwidth=%d\n", rs_out.bandwidth);
+        smartlist_add(chunks, cp);
+      }
 
       /*     Now the exitpolicy summary line. */
       if (rs_out.has_exitsummary && flavor == FLAV_NS) {
-        char buf[MAX_POLICY_LINE_LEN+1];
-        int r = tor_snprintf(buf, sizeof(buf), "p %s\n", rs_out.exitsummary);
-        if (r<0) {
-          log_warn(LD_BUG, "Not enough space in buffer for exitpolicy line.");
-          *buf = '\0';
-        }
-        smartlist_add(chunks, tor_strdup(buf));
-      };
+        tor_asprintf(&buf, "p %s\n", rs_out.exitsummary);
+        smartlist_add(chunks, buf);
+      }
 
       /* And the loop is over and we move on to the next router */
     }
@@ -1757,10 +1747,12 @@ networkstatus_compute_consensus(smartlist_t *votes,
     // Parse params, extract BW_WEIGHT_SCALE if present
     // DO NOT use consensus_param_bw_weight_scale() in this code!
     // The consensus is not formed yet!
-    if (strcmpstart(params, "bwweightscale=") == 0)
-      bw_weight_param = params;
-    else
-      bw_weight_param = strstr(params, " bwweightscale=");
+    if (params) {
+      if (strcmpstart(params, "bwweightscale=") == 0)
+        bw_weight_param = params;
+      else
+        bw_weight_param = strstr(params, " bwweightscale=");
+    }
 
     if (bw_weight_param) {
       int ok=0;
@@ -1793,8 +1785,9 @@ networkstatus_compute_consensus(smartlist_t *votes,
     size_t digest_len =
       flavor == FLAV_NS ? DIGEST_LEN : DIGEST256_LEN;
     const char *algname = crypto_digest_algorithm_get_name(digest_alg);
+    char *buf = NULL;
+    char sigbuf[4096];
 
-    char buf[4096];
     smartlist_add(chunks, tor_strdup("directory-signature "));
 
     /* Compute the hash of the chunks. */
@@ -1806,20 +1799,23 @@ networkstatus_compute_consensus(smartlist_t *votes,
 
     /* add the junk that will go at the end of the line. */
     if (flavor == FLAV_NS) {
-      tor_snprintf(buf, sizeof(buf), "%s %s\n", fingerprint,
+      tor_asprintf(&buf, "%s %s\n", fingerprint,
                    signing_key_fingerprint);
     } else {
-      tor_snprintf(buf, sizeof(buf), "%s %s %s\n",
+      tor_asprintf(&buf, "%s %s %s\n",
                    algname, fingerprint,
                    signing_key_fingerprint);
     }
+    smartlist_add(chunks, buf);
     /* And the signature. */
-    if (router_append_dirobj_signature(buf, sizeof(buf), digest, digest_len,
+    sigbuf[0] = '\0';
+    if (router_append_dirobj_signature(sigbuf, sizeof(sigbuf),
+                                       digest, digest_len,
                                        signing_key)) {
       log_warn(LD_BUG, "Couldn't sign consensus networkstatus.");
       return NULL; /* This leaks, but it should never happen. */
     }
-    smartlist_add(chunks, tor_strdup(buf));
+    smartlist_add(chunks, tor_strdup(sigbuf));
 
     if (legacy_id_key_digest && legacy_signing_key && consensus_method >= 3) {
       smartlist_add(chunks, tor_strdup("directory-signature "));
@@ -1828,19 +1824,22 @@ networkstatus_compute_consensus(smartlist_t *votes,
       crypto_pk_get_fingerprint(legacy_signing_key,
                                 signing_key_fingerprint, 0);
       if (flavor == FLAV_NS) {
-        tor_snprintf(buf, sizeof(buf), "%s %s\n", fingerprint,
+        tor_asprintf(&buf, "%s %s\n", fingerprint,
                      signing_key_fingerprint);
       } else {
-        tor_snprintf(buf, sizeof(buf), "%s %s %s\n",
+        tor_asprintf(&buf, "%s %s %s\n",
                      algname, fingerprint,
                      signing_key_fingerprint);
       }
-      if (router_append_dirobj_signature(buf, sizeof(buf), digest, digest_len,
+      smartlist_add(chunks, buf);
+      sigbuf[0] = '\0';
+      if (router_append_dirobj_signature(sigbuf, sizeof(sigbuf),
+                                         digest, digest_len,
                                          legacy_signing_key)) {
         log_warn(LD_BUG, "Couldn't sign consensus networkstatus.");
         return NULL; /* This leaks, but it should never happen. */
       }
-      smartlist_add(chunks, tor_strdup(buf));
+      smartlist_add(chunks, tor_strdup(sigbuf));
     }
   }
 
diff --git a/src/or/dns.c b/src/or/dns.c
index 54acef4be29a2032efbaaca2647cb6bf25b37e31..192a1929d7ba7df72236736172002b1b1aa18fdc 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -260,6 +260,14 @@ dns_reset(void)
 {
   or_options_t *options = get_options();
   if (! server_mode(options)) {
+
+    if (!the_evdns_base) {
+      if (!(the_evdns_base = evdns_base_new(tor_libevent_get_base(), 0))) {
+        log_err(LD_BUG, "Couldn't create an evdns_base");
+        return -1;
+      }
+    }
+
     evdns_base_clear_nameservers_and_suspend(the_evdns_base);
     evdns_base_search_clear(the_evdns_base);
     nameservers_configured = 0;
@@ -1377,6 +1385,8 @@ launch_resolve(edge_connection_t *exitconn)
 
   r = tor_addr_parse_reverse_lookup_name(
                             &a, exitconn->_base.address, AF_UNSPEC, 0);
+
+  tor_assert(the_evdns_base);
   if (r == 0) {
     log_info(LD_EXIT, "Launching eventdns request for %s",
              escaped_safe_str(exitconn->_base.address));
@@ -1546,6 +1556,7 @@ launch_wildcard_check(int min_len, int max_len, const char *suffix)
   log_info(LD_EXIT, "Testing whether our DNS server is hijacking nonexistent "
            "domains with request for bogus hostname \"%s\"", addr);
 
+  tor_assert(the_evdns_base);
   req = evdns_base_resolve_ipv4(
                          the_evdns_base,
                          /* This "addr" tells us which address to resolve */
@@ -1576,6 +1587,7 @@ launch_test_addresses(int fd, short event, void *args)
    * be an exit server.*/
   if (!options->ServerDNSTestAddresses)
     return;
+  tor_assert(the_evdns_base);
   SMARTLIST_FOREACH_BEGIN(options->ServerDNSTestAddresses,
                           const char *, address) {
     char *a = tor_strdup(address);
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 0f4805ec9d0e8bf7c3dfa2942ad9a4a6734497d0..b5a0374c492b1be6617a032b8ff0ae6b9fac723d 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -815,7 +815,6 @@ geoip_get_client_history(time_t now, geoip_client_action_t action,
   if (!geoip_is_loaded())
     return NULL;
   if (client_history_starts < (now - min_observation_time)) {
-    char buf[32];
     smartlist_t *chunks = NULL;
     smartlist_t *entries = NULL;
     int n_countries = geoip_get_n_countries();
@@ -860,9 +859,10 @@ geoip_get_client_history(time_t now, geoip_client_action_t action,
     /* Build the result. */
     chunks = smartlist_create();
     SMARTLIST_FOREACH(entries, c_hist_t *, ch, {
-        tor_snprintf(buf, sizeof(buf), "%s=%u", ch->country, ch->total);
-        smartlist_add(chunks, tor_strdup(buf));
-      });
+        char *buf=NULL;
+        tor_asprintf(&buf, "%s=%u", ch->country, ch->total);
+        smartlist_add(chunks, buf);
+    });
     result = smartlist_join_strings(chunks, ",", 0, NULL);
   done:
     tor_free(counts);
@@ -947,7 +947,7 @@ geoip_get_request_history(time_t now, geoip_client_action_t action)
   SMARTLIST_FOREACH(entries, c_hist_t *, ent, {
       char buf[32];
       tor_snprintf(buf, sizeof(buf), "%s=%u", ent->country, ent->total);
-      smartlist_add(strings, tor_strdup(buf));
+      smartlist_add(strings, buf);
     });
   result = smartlist_join_strings(strings, ",", 0, NULL);
   SMARTLIST_FOREACH(strings, char *, cp, tor_free(cp));
@@ -1105,7 +1105,6 @@ parse_bridge_stats_controller(const char *stats_str, time_t now)
   const char *BRIDGE_IPS_EMPTY_LINE = "bridge-ips\n";
   const char *tmp;
   time_t stats_end_time;
-  size_t controller_len;
   int seconds;
   tor_assert(stats_str);
 
@@ -1147,16 +1146,9 @@ parse_bridge_stats_controller(const char *stats_str, time_t now)
     summary = tor_strdup("");
   }
 
-  controller_len = strlen("TimeStarted=\"\" CountrySummary=") +
-                          strlen(summary) + 42;
-  controller_str = tor_malloc(controller_len);
-  if (tor_snprintf(controller_str, controller_len,
-                   "TimeStarted=\"%s\" CountrySummary=%s",
-                   stats_start_str, summary) < 0) {
-    tor_free(controller_str);
-    tor_free(summary);
-    return NULL;
-  }
+  tor_asprintf(&controller_str,
+               "TimeStarted=\"%s\" CountrySummary=%s",
+               stats_start_str, summary);
   tor_free(summary);
   return controller_str;
 }
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 8110c78388c4c486cb3db792952eda9c0483d76d..bb4ee4cb564f542381f96854e626294173bf0478 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -1530,7 +1530,6 @@ networkstatus_set_current_consensus(const char *consensus,
       !directory_caches_dir_info(get_options())) {
     /* This consensus is totally boring to us: we won't use it, and we won't
      * serve it.  Drop it. */
-    result = -1;
     goto done;
   }
 
diff --git a/src/or/or.h b/src/or/or.h
index cf27520f1b96fbde7edb09c1735f513c114d8b4c..7c1e5a7a097b801d8be7f695db7ce95e0d53dd5d 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -2462,6 +2462,11 @@ typedef struct {
   int ConstrainedSockets; /**< Shrink xmit and recv socket buffers. */
   uint64_t ConstrainedSockSize; /**< Size of constrained buffers. */
 
+  /** Whether we should drop exit streams from Tors that we don't know
+   * are relays. XXX022 In here for 0.2.2.11 as a temporary test before
+   * we switch over to putting it in consensusparams. -RD */
+  int RefuseUnknownExits;
+
   /** Application ports that require all nodes in circ to have sufficient
    * uptime. */
   smartlist_t *LongLivedPorts;
@@ -3532,6 +3537,7 @@ int connection_or_process_inbuf(or_connection_t *conn);
 int connection_or_flushed_some(or_connection_t *conn);
 int connection_or_finished_flushing(or_connection_t *conn);
 int connection_or_finished_connecting(or_connection_t *conn);
+int connection_or_digest_is_known_relay(const char *id_digest);
 
 void connection_or_connect_failed(or_connection_t *conn,
                                   int reason, const char *msg);
@@ -4487,6 +4493,9 @@ 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);
 
+enum bandwidth_weight_rule_t;
+const char *bandwidth_weight_rule_to_string(enum bandwidth_weight_rule_t rule);
+
 /********************************* relay.c ***************************/
 
 extern uint64_t stats_n_relay_cells_relayed;
@@ -4957,7 +4966,7 @@ uint32_t router_get_advertised_bandwidth_capped(routerinfo_t *router);
 
 /** Possible ways to weight routers when choosing one randomly.  See
  * routerlist_sl_choose_by_bandwidth() for more information.*/
-typedef enum {
+typedef enum bandwidth_weight_rule_t {
   NO_WEIGHTING, WEIGHT_FOR_EXIT, WEIGHT_FOR_MID, WEIGHT_FOR_GUARD,
   WEIGHT_FOR_DIR
 } bandwidth_weight_rule_t;
diff --git a/src/or/reasons.c b/src/or/reasons.c
index 46483b2770f6197aea5169fd29d8f3cd08b5e240..e1c64ebff38d67435479693440b5969df9ecc036 100644
--- a/src/or/reasons.c
+++ b/src/or/reasons.c
@@ -372,3 +372,24 @@ socks5_response_code_to_string(uint8_t code)
   }
 }
 
+/** Return a string corresponding to a bandwidht_weight_rule_t */
+const char *
+bandwidth_weight_rule_to_string(bandwidth_weight_rule_t rule)
+{
+  switch (rule)
+    {
+    case NO_WEIGHTING:
+      return "no weighting";
+    case WEIGHT_FOR_EXIT:
+      return "weight as exit";
+    case WEIGHT_FOR_MID:
+      return "weight as middle node";
+    case WEIGHT_FOR_GUARD:
+      return "weight as guard";
+    case WEIGHT_FOR_DIR:
+      return "weight as directory";
+    default:
+      return "unknown rule";
+  }
+}
+
diff --git a/src/or/relay.c b/src/or/relay.c
index 599d3d9c80eb0c1a2f92a33704d42c4975a98f92..fab2d8896e5bae88c91ae522d5b31fafe36e6d30 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -1865,9 +1865,9 @@ cell_ewma_set_scale_factor(or_options_t *options, networkstatus_t *consensus)
     source = "CircuitPriorityHalflife in configuration";
   } else if (consensus &&
              (halflife_ms = networkstatus_get_param(
-                   consensus, "CircPriorityHalflifeMsec", -1) >= 0)) {
+                   consensus, "CircuitPriorityHalflifeMsec", -1)) >= 0) {
     halflife = ((double)halflife_ms)/1000.0;
-    source = "CircPriorityHalflifeMsec in consensus";
+    source = "CircuitPriorityHalflifeMsec in consensus";
   } else {
     halflife = EWMA_DEFAULT_HALFLIFE;
     source = "Default value";
diff --git a/src/or/rephist.c b/src/or/rephist.c
index c8da8dfe2d4fc56bd908f8553c8e1bfe007b0eb6..0e55db2d636e8ce6f1be98c9cbc8e0ec6f9494e8 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -793,12 +793,12 @@ rep_hist_record_mtbf_data(time_t now, int missing_means_down)
 static char *
 rep_hist_format_router_status(or_history_t *hist, time_t now)
 {
-  char buf[1024];
   char sor_buf[ISO_TIME_LEN+1];
   char sod_buf[ISO_TIME_LEN+1];
   double wfu;
   double mtbf;
   int up = 0, down = 0;
+  char *cp = NULL;
 
   if (hist->start_of_run) {
     format_iso_time(sor_buf, hist->start_of_run);
@@ -811,7 +811,7 @@ rep_hist_format_router_status(or_history_t *hist, time_t now)
 
   wfu = get_weighted_fractional_uptime(hist, now);
   mtbf = get_stability(hist, now);
-  tor_snprintf(buf, sizeof(buf),
+  tor_asprintf(&cp,
                "%s%s%s"
                "%s%s%s"
                "wfu %0.3lf\n"
@@ -829,8 +829,7 @@ rep_hist_format_router_status(or_history_t *hist, time_t now)
                hist->weighted_run_length,
                hist->total_run_weights
                );
-
-  return tor_strdup(buf);
+  return cp;
 }
 
 /** The last stability analysis document that we created, or NULL if we never
@@ -2140,8 +2139,7 @@ rep_hist_buffer_stats_write(time_t now)
       number_of_circuits, i;
   double queued_cells[SHARES], time_in_queue[SHARES];
   smartlist_t *str_build = smartlist_create();
-  char *str = NULL;
-  char buf[32];
+  char *str = NULL, *buf=NULL;
   circuit_t *circ;
   /* add current circuits to stats */
   for (circ = _circuit_get_global_list(); circ; circ = circ->next)
@@ -2190,9 +2188,9 @@ rep_hist_buffer_stats_write(time_t now)
               (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 :
+    tor_asprintf(&buf,"%d", !circs_in_share[i] ? 0 :
                  processed_cells[i] / circs_in_share[i]);
-    smartlist_add(str_build, tor_strdup(buf));
+    smartlist_add(str_build, buf);
   }
   str = smartlist_join_strings(str_build, ",", 0, NULL);
   if (fprintf(out, "cell-processed-cells %s\n", str) < 0)
@@ -2201,9 +2199,9 @@ rep_hist_buffer_stats_write(time_t now)
   SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
   smartlist_clear(str_build);
   for (i = 0; i < SHARES; i++) {
-    tor_snprintf(buf, sizeof(buf), "%.2f", circs_in_share[i] == 0 ? 0.0 :
+    tor_asprintf(&buf, "%.2f", circs_in_share[i] == 0 ? 0.0 :
                  queued_cells[i] / (double) circs_in_share[i]);
-    smartlist_add(str_build, tor_strdup(buf));
+    smartlist_add(str_build, buf);
   }
   str = smartlist_join_strings(str_build, ",", 0, NULL);
   if (fprintf(out, "cell-queued-cells %s\n", str) < 0)
@@ -2212,9 +2210,9 @@ rep_hist_buffer_stats_write(time_t now)
   SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
   smartlist_clear(str_build);
   for (i = 0; i < SHARES; i++) {
-    tor_snprintf(buf, sizeof(buf), "%.0f", circs_in_share[i] == 0 ? 0.0 :
+    tor_asprintf(&buf, "%.0f", circs_in_share[i] == 0 ? 0.0 :
                  time_in_queue[i] / (double) circs_in_share[i]);
-    smartlist_add(str_build, tor_strdup(buf));
+    smartlist_add(str_build, buf);
   }
   str = smartlist_join_strings(str_build, ",", 0, NULL);
   if (fprintf(out, "cell-time-in-queue %s\n", str) < 0)
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 0173c27e4e26e74c3189ac4d494e8469bac1e68a..13123e44112442a37fafb463446e651057b7c43a 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -1577,7 +1577,8 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl,
   if (smartlist_len(sl) == 0) {
     log_info(LD_CIRC,
              "Empty routerlist passed in to consensus weight node "
-             "selection for rule %d", rule);
+             "selection for rule %s",
+             bandwidth_weight_rule_to_string(rule));
     return NULL;
   }
 
@@ -1695,15 +1696,16 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl,
     weighted_bw += weight*this_bw;
   }
 
-  log_debug(LD_CIRC, "Choosing node for rule %d based on weights "
-            "Wg=%lf Wm=%lf We=%lf Wd=%lf with total bw %lf", rule,
+  log_debug(LD_CIRC, "Choosing node for rule %s based on weights "
+            "Wg=%lf Wm=%lf We=%lf Wd=%lf with total bw %lf",
+            bandwidth_weight_rule_to_string(rule),
             Wg, Wm, We, Wd, weighted_bw);
 
   /* If there is no bandwidth, choose at random */
   if (DBL_TO_U64(weighted_bw) == 0) {
     log_warn(LD_CIRC,
-             "Weighted bandwidth is %lf in node selection for rule %d",
-             weighted_bw, rule);
+             "Weighted bandwidth is %lf in node selection for rule %s",
+             weighted_bw, bandwidth_weight_rule_to_string(rule));
     tor_free(bandwidths);
     return smartlist_choose(sl);
   }
@@ -1783,8 +1785,8 @@ smartlist_choose_by_bandwidth(smartlist_t *sl, bandwidth_weight_rule_t rule,
 
   if (smartlist_len(sl) == 0) {
     log_info(LD_CIRC,
-             "Empty routerlist passed in to old node selection for rule %d",
-             rule);
+             "Empty routerlist passed in to old node selection for rule %s",
+             bandwidth_weight_rule_to_string(rule));
     return NULL;
   }
 
diff --git a/src/test/Makefile.am b/src/test/Makefile.am
index 7df13da51e514e8454eadb613c9bafd660bb8993..546fa2f4b7ece04031f3f9af28a43958c92a98ef 100644
--- a/src/test/Makefile.am
+++ b/src/test/Makefile.am
@@ -25,6 +25,6 @@ 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 @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
+	@TOR_ZLIB_LIBS@ -lm @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
 
 noinst_HEADERS = tinytest.h tinytest_macros.h test.h
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 34a6f4d66216a13d8ce9e32ea1ca8ba924e8e4e9..bba96325c46b8bdb70bf77f0ffb1bc8e57e76c1b 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -448,6 +448,11 @@ test_util_threads(void)
   char *s1 = NULL, *s2 = NULL;
   int done = 0, timedout = 0;
   time_t started;
+#ifndef MS_WINDOWS
+  struct timeval tv;
+  tv.tv_sec=0;
+  tv.tv_usec=10;
+#endif
 #ifndef TOR_IS_MULTITHREADED
   /* Skip this test if we aren't threading. We should be threading most
    * everywhere by now. */
@@ -477,14 +482,18 @@ test_util_threads(void)
       timedout = done = 1;
     }
     tor_mutex_release(_thread_test_mutex);
+#ifndef MS_WINDOWS
+    /* Prevent the main thread from starving the worker threads. */
+    select(0, NULL, NULL, NULL, &tv);
+#endif
   }
-  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);
 
+  tor_mutex_free(_thread_test_mutex);
+
   if (timedout) {
     printf("\nTimed out: %d %d", t1_count, t2_count);
     test_assert(strmap_get(_thread_test_strmap, "thread 1"));
@@ -1050,6 +1059,51 @@ test_util_find_str_at_start_of_line(void *ptr)
   ;
 }
 
+static void
+test_util_asprintf(void *ptr)
+{
+#define LOREMIPSUM                                              \
+  "Lorem ipsum dolor sit amet, consectetur adipisicing elit"
+  char *cp=NULL, *cp2=NULL;
+  int r;
+  (void)ptr;
+
+  /* empty string. */
+  r = tor_asprintf(&cp, "%s", "");
+  tt_assert(cp);
+  tt_int_op(r, ==, strlen(cp));
+  tt_str_op(cp, ==, "");
+
+  /* Short string with some printing in it. */
+  r = tor_asprintf(&cp2, "First=%d, Second=%d", 101, 202);
+  tt_assert(cp2);
+  tt_int_op(r, ==, strlen(cp2));
+  tt_str_op(cp2, ==, "First=101, Second=202");
+  tt_assert(cp != cp2);
+  tor_free(cp);
+  tor_free(cp2);
+
+  /* Glass-box test: a string exactly 128 characters long. */
+  r = tor_asprintf(&cp, "Lorem1: %sLorem2: %s", LOREMIPSUM, LOREMIPSUM);
+  tt_assert(cp);
+  tt_int_op(r, ==, 128);
+  tt_assert(cp[128] == '\0');
+  tt_str_op(cp, ==,
+            "Lorem1: "LOREMIPSUM"Lorem2: "LOREMIPSUM);
+  tor_free(cp);
+
+  /* String longer than 128 characters */
+  r = tor_asprintf(&cp, "1: %s 2: %s 3: %s",
+                   LOREMIPSUM, LOREMIPSUM, LOREMIPSUM);
+  tt_assert(cp);
+  tt_int_op(r, ==, strlen(cp));
+  tt_str_op(cp, ==, "1: "LOREMIPSUM" 2: "LOREMIPSUM" 3: "LOREMIPSUM);
+
+ done:
+  tor_free(cp);
+  tor_free(cp2);
+}
+
 #define UTIL_LEGACY(name)                                               \
   { #name, legacy_test_helper, 0, &legacy_setup, test_util_ ## name }
 
@@ -1071,6 +1125,7 @@ struct testcase_t util_tests[] = {
   UTIL_LEGACY(sscanf),
   UTIL_LEGACY(strtok),
   UTIL_TEST(find_str_at_start_of_line, 0),
+  UTIL_TEST(asprintf, 0),
   END_OF_TESTCASES
 };
 
diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am
index be03f8d892308f02f004cb88d6e90ede36d2464a..1bb5076849e9a1634e8becb59a763fff3dfbbd3b 100644
--- a/src/tools/Makefile.am
+++ b/src/tools/Makefile.am
@@ -9,10 +9,10 @@ tor_gencert_SOURCES = tor-gencert.c
 tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
         @TOR_LDFLAGS_libevent@
 tor_gencert_LDADD = ../common/libor.a ../common/libor-crypto.a \
-        -lm -lz @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
+        -lm @TOR_ZLIB_LIBS@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
 
 tor_checkkey_SOURCES = tor-checkkey.c
 tor_checkkey_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
         @TOR_LDFLAGS_libevent@
 tor_checkkey_LDADD = ../common/libor.a ../common/libor-crypto.a \
-        -lm -lz @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
+        -lm @TOR_ZLIB_LIBS@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h
index 6751ff8f9883b4cd6e2975675ca95eaa2a0d33fc..873ea8804e3af18eb2101118322d80b71c8048b9 100644
--- a/src/win32/orconfig.h
+++ b/src/win32/orconfig.h
@@ -226,5 +226,5 @@
 #define USING_TWOS_COMPLEMENT
 
 /* Version number of package */
-#define VERSION "0.2.2.10-alpha"
+#define VERSION "0.2.2.11-alpha"