Browse Source

Merge remote branch 'origin/master' into bug2046

Steven Murdoch 14 years ago
parent
commit
cc5b6d6cee
63 changed files with 3181 additions and 817 deletions
  1. 2 0
      .gitignore
  2. 55 3
      ChangeLog
  3. 1 0
      Makefile.am
  4. 5 0
      Makefile.nmake
  5. 5 0
      changes/bug1692
  6. 5 0
      changes/bug2930
  7. 5 0
      changes/bug3550
  8. 3 0
      changes/bug3615
  9. 4 0
      changes/bug3643
  10. 6 0
      changes/bug3700
  11. 7 0
      changes/bug3732
  12. 6 0
      changes/bug3747
  13. 4 0
      changes/fmt_addr
  14. 3 0
      changes/geoip-august2011
  15. 3 0
      changes/le-win-threads
  16. 3 0
      changes/nmake
  17. 9 0
      changes/optimistic-client
  18. 22 0
      changes/prop171
  19. 12 0
      changes/require-le-2.0.13
  20. 11 2
      configure.in
  21. 89 25
      doc/tor.1.txt
  22. 1 1
      src/common/Makefile.am
  23. 20 0
      src/common/Makefile.nmake
  24. 4 2
      src/common/address.c
  25. 1 1
      src/common/compat.h
  26. 4 1
      src/common/compat_libevent.c
  27. 0 4
      src/common/torgzip.c
  28. 9 0
      src/common/torint.h
  29. 16 0
      src/common/tortls.c
  30. 72 3
      src/common/util.c
  31. 7 0
      src/common/util.h
  32. 466 107
      src/config/geoip
  33. 1 1
      src/or/Makefile.am
  34. 28 0
      src/or/Makefile.nmake
  35. 88 22
      src/or/buffers.c
  36. 19 0
      src/or/buffers.h
  37. 12 1
      src/or/circuitlist.c
  38. 170 22
      src/or/circuituse.c
  39. 439 67
      src/or/config.c
  40. 2 0
      src/or/config.h
  41. 315 285
      src/or/connection.c
  42. 1 0
      src/or/connection.h
  43. 270 3
      src/or/connection_edge.c
  44. 10 1
      src/or/connection_edge.h
  45. 37 2
      src/or/control.c
  46. 1 0
      src/or/control.h
  47. 7 1
      src/or/directory.c
  48. 24 7
      src/or/dnsserv.c
  49. 184 124
      src/or/geoip.c
  50. 5 0
      src/or/geoip.h
  51. 19 1
      src/or/main.c
  52. 1 0
      src/or/main.h
  53. 0 6
      src/or/microdesc.c
  54. 0 7
      src/or/networkstatus.c
  55. 188 11
      src/or/or.h
  56. 42 2
      src/or/relay.c
  57. 142 98
      src/or/rephist.c
  58. 4 0
      src/or/rephist.h
  59. 3 5
      src/or/router.c
  60. 3 0
      src/or/routerparse.c
  61. 295 1
      src/test/test.c
  62. 1 1
      src/tools/tor-fw-helper/Makefile.am
  63. 10 0
      src/win32/orconfig.h

+ 2 - 0
.gitignore

@@ -146,6 +146,8 @@
 /src/test/Makefile.in
 /src/test/Makefile.in
 /src/test/test
 /src/test/test
 /src/test/test-child
 /src/test/test-child
+/src/test/test.exe
+/src/test/test-child.exe
 
 
 
 
 # /src/tools/
 # /src/tools/

+ 55 - 3
ChangeLog

@@ -391,7 +391,7 @@ Changes in version 0.2.2.26-beta - 2011-05-17
     - Authorities now clean their microdesc cache periodically and when
     - Authorities now clean their microdesc cache periodically and when
       reading from disk initially, not only when adding new descriptors.
       reading from disk initially, not only when adding new descriptors.
       This prevents a bug where we could lose microdescriptors. Bugfix
       This prevents a bug where we could lose microdescriptors. Bugfix
-      on 0.2.2.6-alpha. 2230
+      on 0.2.2.6-alpha. Fixes bug 2230.
     - Do not crash when our configuration file becomes unreadable, for
     - Do not crash when our configuration file becomes unreadable, for
       example due to a permissions change, between when we start up
       example due to a permissions change, between when we start up
       and when a controller calls SAVECONF. Fixes bug 3135; bugfix
       and when a controller calls SAVECONF. Fixes bug 3135; bugfix
@@ -1019,6 +1019,57 @@ Changes in version 0.2.2.23-alpha - 2011-03-08
       git://git.torproject.org/torspec.git
       git://git.torproject.org/torspec.git
 
 
 
 
+Changes in version 0.2.1.30 - 2011-02-23
+  Tor 0.2.1.30 fixes a variety of less critical bugs. The main other
+  change is a slight tweak to Tor's TLS handshake that makes relays
+  and bridges that run this new version reachable from Iran again.
+  We don't expect this tweak will win the arms race long-term, but it
+  buys us time until we roll out a better solution.
+
+  o Major bugfixes:
+    - Stop sending a CLOCK_SKEW controller status event whenever
+      we fetch directory information from a relay that has a wrong clock.
+      Instead, only inform the controller when it's a trusted authority
+      that claims our clock is wrong. Bugfix on 0.1.2.6-alpha; fixes
+      the rest of bug 1074.
+    - Fix a bounds-checking error that could allow an attacker to
+      remotely crash a directory authority. Bugfix on 0.2.1.5-alpha.
+      Found by "piebeer".
+    - If relays set RelayBandwidthBurst but not RelayBandwidthRate,
+      Tor would ignore their RelayBandwidthBurst setting,
+      potentially using more bandwidth than expected. Bugfix on
+      0.2.0.1-alpha. Reported by Paul Wouters. Fixes bug 2470.
+    - Ignore and warn if the user mistakenly sets "PublishServerDescriptor
+      hidserv" in her torrc. The 'hidserv' argument never controlled
+      publication of hidden service descriptors. Bugfix on 0.2.0.1-alpha.
+
+  o Minor features:
+    - Adjust our TLS Diffie-Hellman parameters to match those used by
+      Apache's mod_ssl.
+    - Update to the February 1 2011 Maxmind GeoLite Country database.
+
+  o Minor bugfixes:
+    - Check for and reject overly long directory certificates and
+      directory tokens before they have a chance to hit any assertions.
+      Bugfix on 0.2.1.28. Found by "doorss".
+    - Bring the logic that gathers routerinfos and assesses the
+      acceptability of circuits into line. This prevents a Tor OP from
+      getting locked in a cycle of choosing its local OR as an exit for a
+      path (due to a .exit request) and then rejecting the circuit because
+      its OR is not listed yet. It also prevents Tor clients from using an
+      OR running in the same instance as an exit (due to a .exit request)
+      if the OR does not meet the same requirements expected of an OR
+      running elsewhere. Fixes bug 1859; bugfix on 0.1.0.1-rc.
+
+  o Packaging changes:
+    - Stop shipping the Tor specs files and development proposal documents
+      in the tarball. They are now in a separate git repository at
+      git://git.torproject.org/torspec.git
+    - Do not include Git version tags as though they are SVN tags when
+      generating a tarball from inside a repository that has switched
+      between branches. Bugfix on 0.2.1.15-rc; fixes bug 2402.
+
+
 Changes in version 0.2.2.22-alpha - 2011-01-25
 Changes in version 0.2.2.22-alpha - 2011-01-25
   Tor 0.2.2.22-alpha fixes a few more less-critical security issues. The
   Tor 0.2.2.22-alpha fixes a few more less-critical security issues. The
   main other change is a slight tweak to Tor's TLS handshake that makes
   main other change is a slight tweak to Tor's TLS handshake that makes
@@ -2679,8 +2730,9 @@ Changes in version 0.2.2.5-alpha - 2009-10-11
   o Major bugfixes:
   o Major bugfixes:
     - Make the tarball compile again. Oops. Bugfix on 0.2.2.4-alpha.
     - Make the tarball compile again. Oops. Bugfix on 0.2.2.4-alpha.
 
 
-  o New directory authorities:
-    - Move dizum to an alternate IP address.
+  o Directory authorities:
+    - Temporarily (just for this release) move dizum to an alternate
+      IP address.
 
 
 
 
 Changes in version 0.2.2.4-alpha - 2009-10-10
 Changes in version 0.2.2.4-alpha - 2009-10-10

+ 1 - 0
Makefile.am

@@ -15,6 +15,7 @@ EXTRA_DIST = \
 	ChangeLog					\
 	ChangeLog					\
 	INSTALL						\
 	INSTALL						\
 	LICENSE						\
 	LICENSE						\
+	Makefile.nmake					\
 	README						\
 	README						\
 	ReleaseNotes					\
 	ReleaseNotes					\
 	tor.spec					\
 	tor.spec					\

+ 5 - 0
Makefile.nmake

@@ -0,0 +1,5 @@
+all:
+	cd src/common
+	$(MAKE) /F Makefile.nmake
+	cd ../../src/or
+	$(MAKE) /F Makefile.nmake

+ 5 - 0
changes/bug1692

@@ -0,0 +1,5 @@
+  o Minor features:
+    - CONF_CHANGED event is provided so that controllers can be notified
+      of any configuration changes made by other controllers/SETCONF/HUP.
+      Implements #1692.
+

+ 5 - 0
changes/bug2930

@@ -0,0 +1,5 @@
+  o Minor features:
+    - Replace files in stats/ rather than appending to them.  Now that we
+      include statistics in extra-info descriptors, it makes no sense to
+      keep old statistics forever.  Implements #2930.
+

+ 5 - 0
changes/bug3550

@@ -0,0 +1,5 @@
+  o Minor bugfixes:
+    - The "--quiet" and "--hush" options now apply not only to Tor's
+      behavior before user-configured logs are added, but also to
+      Tor's behavior in the absense of configured logs. Fixes bug
+      3550; bugfix on 0.2.0.10-alpha.

+ 3 - 0
changes/bug3615

@@ -0,0 +1,3 @@
+  o Minor bugfixes:
+    - Fix a spurious warning when parsing SOCKS requests with
+      bufferevents enabled. Fixes bug 3615; bugfix on 0.2.3.2-alpha.

+ 4 - 0
changes/bug3643

@@ -0,0 +1,4 @@
+  o Minor bugfixes:
+    - Selectively disable deprecation warnings on OS X because Lion started
+      deprecating the shipped copy of openssl. Fixes bug 3643.
+

+ 6 - 0
changes/bug3700

@@ -0,0 +1,6 @@
+  o Minor bugfixes:
+    - Get rid of a harmless warning that could happen on relays running
+      with bufferevents. The warning was caused by someone doing an http
+      request to a relay's orport. Also don't warn for a few related
+      non-errors. Fixes bug 3700; bugfix on 0.2.3.1-alpha.
+

+ 7 - 0
changes/bug3732

@@ -0,0 +1,7 @@
+  o Major bugfixes:
+
+    - Remove an extra pair of quotation marks around the error
+      message in control-port STATUS_GENERAL BUG events.  Bugfix on
+      0.1.2.6-alpha; fixes bug 3732.
+
+

+ 6 - 0
changes/bug3747

@@ -0,0 +1,6 @@
+  o Major bugfixes:
+    - Write control ports to disk only after switching UID and
+      creating the data directory. This way, we don't fail when
+      starting up with a nonexistant DataDirectory and a
+      ControlPortWriteToFile setting based on that directory. Fixes
+      bug 3747; bugfix on Tor 0.2.2.26-beta.

+ 4 - 0
changes/fmt_addr

@@ -0,0 +1,4 @@
+  o Minor bugfixes:
+    - When unable to format an address as a string, report its value
+      as "???" rather than reusing the last formatted address. Bugfix
+      on 0.2.1.5-alpha.

+ 3 - 0
changes/geoip-august2011

@@ -0,0 +1,3 @@
+  o Minor features:
+    - Update to the August 2 2011 Maxmind GeoLite Country database.
+

+ 3 - 0
changes/le-win-threads

@@ -0,0 +1,3 @@
+  o Major bugfixes (IOCP):
+    - When using IOCP on windows, we need to enable Libevent windows threading
+      support. Bugfix on 0.2.3.1-alpha.

+ 3 - 0
changes/nmake

@@ -0,0 +1,3 @@
+  o Minor features (build compatibility):
+    - Limited, experimental support for building with nmake and MSVC.
+

+ 9 - 0
changes/optimistic-client

@@ -0,0 +1,9 @@
+  o Major features:
+    - When using an exit nodes running 0.2.3.1-alpha and later,
+      clients can now "optimistically" send data on a stream before
+      the exit node reports that the stream has opened. This can save
+      a round trip when starting connections with protocols where the
+      client speaks first. This behavior is controlled by a (currently
+      disabled) networkstatus consensus parameter. To turn it on or
+      off manually, use the "OptimisticData" torrc option.  Implements
+      proposal 181; code by Ian Goldberg.

+ 22 - 0
changes/prop171

@@ -0,0 +1,22 @@
+  o Major features:
+    - You can now configure Tor so that streams from different
+      applications are isolated on different circuits, to prevent an
+      attacker who sees your streams leaving an exit node from linking
+      your sessions to one another.  To do this, choose some way to
+      distinguish the applications -- have them connect to different
+      SocksPorts, or have one of them use SOCKS4 while the other uses
+      SOCKS5, or have them pass different authentication strings to
+      the SOCKS proxy.  Then use the new SocksPort syntax to configure
+      the degree of isolation you need. This implements Proposal 171.
+
+  o Minor features:
+    - There's a new syntax for specifying multiple client ports (such as
+      SOCKSPort, TransPort, DNSPort, NATDPort): you can now just declare
+      multiple ...Port entries with full addr:port syntax on each.
+      The old ...ListenAddress format is still supported, but you can't
+      mix it with the new SOCKSPort syntax.
+
+  o Code simplifications and refactoring:
+    - Rewrote the listener-selection logic so that parsing which ports
+      we want to listen on is now separate form binding to the ports
+      we want.

+ 12 - 0
changes/require-le-2.0.13

@@ -0,0 +1,12 @@
+  o Build changes:
+    - Building Tor with bufferevent support now requires Libevent
+      2.0.13-stable or later. Previous versions of Libevent had bugs
+      in SSL-related bufferevents and related issues that would make
+      Tor work badly with bufferevents. Requiring 2.0.13-stable also
+      means that Tor with bufferevents can take advantage of Libevent
+      APIs introduced after 2.0.8-rc.
+
+  o Minor bugfixes:
+    - Use evbuffer_copyout() in inspect_evbuffer(). This fixes a memory
+      leak, and lets Libevent worry about how to best copy data out
+      of a buffer.

+ 11 - 2
configure.in

@@ -438,7 +438,7 @@ int x = 1;
       AC_MSG_CHECKING([whether Libevent is new enough for bufferevents])
       AC_MSG_CHECKING([whether Libevent is new enough for bufferevents])
       AC_COMPILE_IFELSE([AC_LANG_SOURCE([
       AC_COMPILE_IFELSE([AC_LANG_SOURCE([
 #include <event2/event.h>
 #include <event2/event.h>
-#if !defined(LIBEVENT_VERSION_NUMBER) || LIBEVENT_VERSION_NUMBER < 0x02000800
+#if !defined(LIBEVENT_VERSION_NUMBER) || LIBEVENT_VERSION_NUMBER < 0x02000d00
 #error
 #error
 int x = y(zz);
 int x = y(zz);
 #else
 #else
@@ -446,7 +446,7 @@ int x = 1;
 #endif
 #endif
    ])], [ AC_MSG_RESULT([yes]) ],
    ])], [ AC_MSG_RESULT([yes]) ],
       [ AC_MSG_RESULT([no])
       [ AC_MSG_RESULT([no])
-        AC_MSG_ERROR([Libevent does not seem new enough to support bufferevents.  We require 2.0.8-rc or later]) ] )
+        AC_MSG_ERROR([Libevent does not seem new enough to support bufferevents.  We require 2.0.13-stable or later]) ] )
     fi
     fi
   fi
   fi
 fi
 fi
@@ -1123,6 +1123,15 @@ else
   enable_gcc_warnings_advisory=no
   enable_gcc_warnings_advisory=no
 fi
 fi
 
 
+# OS X Lion started deprecating the system openssl. Let's just disable
+# all deprecation warnings on OS X.
+case "$host_os" in
+
+ darwin*)
+    CFLAGS="$CFLAGS -Wno-deprecated-declarations"
+    ;;
+esac
+
 # Add some more warnings which we use in development but not in the
 # Add some more warnings which we use in development but not in the
 # released versions.  (Some relevant gcc versions can't handle these.)
 # released versions.  (Some relevant gcc versions can't handle these.)
 if test x$enable_gcc_warnings = xyes || test x$enable_gcc_warnings_advisory = xyes; then
 if test x$enable_gcc_warnings = xyes || test x$enable_gcc_warnings_advisory = xyes; then

+ 89 - 25
doc/tor.1.txt

@@ -464,7 +464,7 @@ CLIENT OPTIONS
 --------------
 --------------
 
 
 The following options are useful only for clients (that is, if
 The following options are useful only for clients (that is, if
-**SocksPort** is non-zero):
+**SocksPort**, **TransPort**, **DNSPort**, or **NATDPort** is non-zero):
 
 
 **AllowInvalidNodes** **entry**|**exit**|**middle**|**introduction**|**rendezvous**|**...**::
 **AllowInvalidNodes** **entry**|**exit**|**middle**|**introduction**|**rendezvous**|**...**::
     If some Tor servers are obviously not working right, the directory
     If some Tor servers are obviously not working right, the directory
@@ -682,17 +682,49 @@ The following options are useful only for clients (that is, if
     the same circuit. Currently, two addresses are "too close" if they lie in
     the same circuit. Currently, two addresses are "too close" if they lie in
     the same /16 range. (Default: 1)
     the same /16 range. (Default: 1)
 
 
-**SocksPort** __PORT__|**auto**::
-    Advertise this port to listen for connections from Socks-speaking
+**SOCKSPort** \['address':]__port__|**auto** [_isolation flags_]::
+    Open this port to listen for connections from SOCKS-speaking
     applications. Set this to 0 if you don't want to allow application
     applications. Set this to 0 if you don't want to allow application
     connections via SOCKS. Set it to "auto" to have Tor pick a port for
     connections via SOCKS. Set it to "auto" to have Tor pick a port for
-    you. (Default: 9050)
-
-**SocksListenAddress** __IP__[:__PORT__]::
+    you. This directive can be specified multiple times to bind
+    to multiple addresses/ports. (Default: 9050) +
+ +
+    The _isolation flags_ arguments give Tor rules for which streams
+    received on this SOCKSPort are allowed to share circuits with one
+    another.  Recognized isolation flags are:
+    **IsolateClientAddr**;;
+        Don't share a circuits with streams from a different
+        client address.  (On by default and strongly recommended;
+        you can disable it with **NoIsolateClientAddr**.)
+    **IsolateSOCKSAuth**;;
+        Don't share a circuits with streams for which different
+        SOCKS authentication was provided. (On by default;
+        you can disable it with **NoIsolateSOCKSAuth**.)
+    **IsolateClientProtocol**;;
+        Don't share circuits with streams using a different protocol.
+        (SOCKS 4, SOCKS 5, TransPort connections, NATDPort connections,
+        and DNSPort requests are all considered to be different protocols.)
+    **IsolateDestPort**;;
+        Don't share a circuits with streams targetting a different
+        destination port.
+    **IsolateDestAddr**;;
+        Don't share a circuits with streams targetting a different
+        destination address.
+    **SessionGroup=**__INT__;;
+        If no other isolation rules would prevent it, allow streams
+        on this port to share circuits with streams from every other
+        port with the same session group.  (By default, streams received
+        on different ports are always isolated from one another.)
+
+**SOCKSListenAddress** __IP__[:__PORT__]::
     Bind to this address to listen for connections from Socks-speaking
     Bind to this address to listen for connections from Socks-speaking
     applications. (Default: 127.0.0.1) You can also specify a port (e.g.
     applications. (Default: 127.0.0.1) You can also specify a port (e.g.
     192.168.0.1:9100). This directive can be specified multiple times to bind
     192.168.0.1:9100). This directive can be specified multiple times to bind
-    to multiple addresses/ports.
+    to multiple addresses/ports.  (DEPRECATED: As of 0.2.3.x-alpha, you can
+    now use multiple SOCKSPort entries, and provide addresses for SOCKSPort
+    entries, so SOCKSListenAddress no longer has a purpose.  For backward
+    compatibility, SOCKSListenAddress is only allowed when SOCKSPort is just
+    a port number.)
 
 
 **SocksPolicy** __policy__,__policy__,__...__::
 **SocksPolicy** __policy__,__policy__,__...__::
     Set an entrance policy for this server, to limit who can connect to the
     Set an entrance policy for this server, to limit who can connect to the
@@ -795,28 +827,44 @@ The following options are useful only for clients (that is, if
     operating as a relay, and it will never use the public key step if it
     operating as a relay, and it will never use the public key step if it
     doesn't yet know the onion key of the first hop. (Default: 1)
     doesn't yet know the onion key of the first hop. (Default: 1)
 
 
-**TransPort** __PORT__|**auto**::
-    If non-zero, enables transparent proxy support on __PORT__ (by convention,
-    9040). Requires OS support for transparent proxies, such as BSDs' pf or
+**TransPort**  \['address':]__port__|**auto** [_isolation flags_]::
+    Open this port to listen for transparent proxy connections.  Set this to
+    0 if you don't want to allow transparent proxy connections.  Set the port
+    to "auto" to have Tor pick a port for you. This directive can be
+    specified multiple times to bind to multiple addresses/ports.  See 
+    SOCKSPort for an explanation of isolation flags. +
+ +
+    TransPort requires OS support for transparent proxies, such as BSDs' pf or
     Linux's IPTables. If you're planning to use Tor as a transparent proxy for
     Linux's IPTables. If you're planning to use Tor as a transparent proxy for
     a network, you'll want to examine and change VirtualAddrNetwork from the
     a network, you'll want to examine and change VirtualAddrNetwork from the
     default setting. You'll also want to set the TransListenAddress option for
     default setting. You'll also want to set the TransListenAddress option for
-    the network you'd like to proxy.  Set it to "auto" to have Tor pick a
-    port for you.  (Default: 0).
+    the network you'd like to proxy. (Default: 0).
 
 
 **TransListenAddress** __IP__[:__PORT__]::
 **TransListenAddress** __IP__[:__PORT__]::
     Bind to this address to listen for transparent proxy connections. (Default:
     Bind to this address to listen for transparent proxy connections. (Default:
     127.0.0.1). This is useful for exporting a transparent proxy server to an
     127.0.0.1). This is useful for exporting a transparent proxy server to an
-    entire network.
-
-**NATDPort** __PORT__|**auto**::
-    Allow old versions of ipfw (as included in old versions of FreeBSD, etc.)
-    to send connections through Tor using the NATD protocol. This option is
-    only for people who cannot use TransPort.  Set it to "auto" to have Tor
-    pick a port for you. (Default: 0)
+    entire network. (DEPRECATED: As of 0.2.3.x-alpha, you can
+    now use multiple TransPort entries, and provide addresses for TransPort
+    entries, so TransListenAddress no longer has a purpose.  For backward
+    compatibility, TransListenAddress is only allowed when TransPort is just
+    a port number.)
+
+**NATDPort** \['address':]__port__|**auto** [_isolation flags_]::
+    Open this port to listen for connections from old versions of ipfw (as
+    included in old versions of FreeBSD, etc) using the NATD protocol.
+    Use 0 if you don't want to allow NATD connections.  Set the port
+    to "auto" to have Tor pick a port for you. This directive can be
+    specified multiple times to bind to multiple addresses/ports.  See
+    SOCKSPort for an explanation of isolation flags. +
+ +
+    This option is only for people who cannot use TransPort. (Default: 0)
 
 
 **NATDListenAddress** __IP__[:__PORT__]::
 **NATDListenAddress** __IP__[:__PORT__]::
-    Bind to this address to listen for NATD connections. (Default: 127.0.0.1).
+    Bind to this address to listen for NATD connections. (DEPRECATED: As of
+    0.2.3.x-alpha, you can now use multiple NATDPort entries, and provide
+    addresses for NATDPort entries, so NATDListenAddress no longer has a
+    purpose.  For backward compatibility, NATDListenAddress is only allowed
+    when NATDPort is just a port number.)
 
 
 **AutomapHostsOnResolve** **0**|**1**::
 **AutomapHostsOnResolve** **0**|**1**::
     When this option is enabled, and we get a request to resolve an address
     When this option is enabled, and we get a request to resolve an address
@@ -829,13 +877,19 @@ The following options are useful only for clients (that is, if
     A comma-separated list of suffixes to use with **AutomapHostsOnResolve**.
     A comma-separated list of suffixes to use with **AutomapHostsOnResolve**.
     The "." suffix is equivalent to "all addresses." (Default: .exit,.onion).
     The "." suffix is equivalent to "all addresses." (Default: .exit,.onion).
 
 
-**DNSPort** __PORT__|**auto**::
-    If non-zero, Tor listens for UDP DNS requests on this port and resolves
-    them anonymously.  Set it to "auto" to have Tor pick a port for
-    you. (Default: 0).
+**DNSPort** \['address':]__port__|**auto** [_isolation flags_]::
+    If non-zero, open this port to listen for UDP DNS requests, and resolve
+    them anonymously.  Set the port to "auto" to have Tor pick a port for
+    you. This directive can be specified multiple times to bind to multiple
+    addresses/ports. See SOCKSPort for an explanation of isolation
+    flags. (Default: 0).
 
 
 **DNSListenAddress** __IP__[:__PORT__]::
 **DNSListenAddress** __IP__[:__PORT__]::
-    Bind to this address to listen for DNS connections. (Default: 127.0.0.1).
+    Bind to this address to listen for DNS connections. (DEPRECATED: As of
+    0.2.3.x-alpha, you can now use multiple DNSPort entries, and provide
+    addresses for DNSPort entries, so DNSListenAddress no longer has a
+    purpose.  For backward compatibility, DNSListenAddress is only allowed
+    when DNSPort is just a port number.)
 
 
 **ClientDNSRejectInternalAddresses** **0**|**1**::
 **ClientDNSRejectInternalAddresses** **0**|**1**::
     If true, Tor does not believe any anonymously retrieved DNS answer that
     If true, Tor does not believe any anonymously retrieved DNS answer that
@@ -876,6 +930,16 @@ The following options are useful only for clients (that is, if
     that have the **AllowSingleHopExits** option turned on to build
     that have the **AllowSingleHopExits** option turned on to build
     one-hop Tor connections.  (Default: 0)
     one-hop Tor connections.  (Default: 0)
 
 
+**OptimisticData** **0**|**1**|**auto**::
+    When this option is set, and Tor is using an exit node that supports
+    the feature, it will try optimistically to send data to the exit node
+    without waiting for the exit node to report whether the connection
+    succeeded.  This can save a round-trip time for protocols like HTTP
+    where the client talks first.  If OptimisticData is set to **auto**,
+    Tor will look at the UseOptimisticData parameter in the networkstatus.
+    (Default: auto)
+
+
 SERVER OPTIONS
 SERVER OPTIONS
 --------------
 --------------
 
 

+ 1 - 1
src/common/Makefile.am

@@ -1,7 +1,7 @@
 
 
 noinst_LIBRARIES = libor.a libor-crypto.a libor-event.a
 noinst_LIBRARIES = libor.a libor-crypto.a libor-event.a
 
 
-EXTRA_DIST = common_sha1.i sha256.c
+EXTRA_DIST = common_sha1.i sha256.c Makefile.nmake
 
 
 #CFLAGS  = -Wall -Wpointer-arith -O2
 #CFLAGS  = -Wall -Wpointer-arith -O2
 
 

+ 20 - 0
src/common/Makefile.nmake

@@ -0,0 +1,20 @@
+all: libor.lib libor-crypto.lib libor-event.lib
+
+CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include
+
+LIBOR_OBJECTS = address.obj compat.obj container.obj di_ops.obj \
+	log.obj memarea.obj mempool.obj procmon.obj util.obj \
+	util_codedigest.obj
+
+LIBOR_CRYPTO_OBJECTS = aes.obj crypto.obj torgzip.obj tortls.obj
+
+LIBOR_EVENT_OBJECTS = compat_libevent.obj
+
+libor.lib: $(LIBOR_OBJECTS)
+	lib $(LIBOR_OBJECTS) /out:libor.lib
+
+libor-crypto.lib: $(LIBOR_CRYPTO_OBJECTS)
+	lib $(LIBOR_CRYPTO_OBJECTS) /out:libor-crypto.lib
+
+libor-event.lib:  $(LIBOR_EVENT_OBJECTS)
+	lib $(LIBOR_EVENT_OBJECTS) /out:libor-event.lib

+ 4 - 2
src/common/address.c

@@ -958,8 +958,10 @@ fmt_addr(const tor_addr_t *addr)
 {
 {
   static char buf[TOR_ADDR_BUF_LEN];
   static char buf[TOR_ADDR_BUF_LEN];
   if (!addr) return "<null>";
   if (!addr) return "<null>";
-  tor_addr_to_str(buf, addr, sizeof(buf), 0);
-  return buf;
+  if (tor_addr_to_str(buf, addr, sizeof(buf), 0))
+    return buf;
+  else
+    return "???";
 }
 }
 
 
 /** Like fmt_addr(), but takes <b>addr</b> as a host-order IPv4
 /** Like fmt_addr(), but takes <b>addr</b> as a host-order IPv4

+ 1 - 1
src/common/compat.h

@@ -401,7 +401,7 @@ typedef int socklen_t;
 
 
 #ifdef MS_WINDOWS
 #ifdef MS_WINDOWS
 #define tor_socket_t intptr_t
 #define tor_socket_t intptr_t
-#define SOCKET_OK(s) ((s) != INVALID_SOCKET)
+#define SOCKET_OK(s) ((unsigned)(s) != INVALID_SOCKET)
 #else
 #else
 #define tor_socket_t int
 #define tor_socket_t int
 #define SOCKET_OK(s) ((s) >= 0)
 #define SOCKET_OK(s) ((s) >= 0)

+ 4 - 1
src/common/compat_libevent.c

@@ -19,6 +19,7 @@
 
 
 #ifdef HAVE_EVENT2_EVENT_H
 #ifdef HAVE_EVENT2_EVENT_H
 #include <event2/event.h>
 #include <event2/event.h>
+#include <event2/thread.h>
 #else
 #else
 #include <event.h>
 #include <event.h>
 #endif
 #endif
@@ -183,8 +184,10 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg)
     struct event_config *cfg = event_config_new();
     struct event_config *cfg = event_config_new();
 
 
 #if defined(MS_WINDOWS) && defined(USE_BUFFEREVENTS)
 #if defined(MS_WINDOWS) && defined(USE_BUFFEREVENTS)
-    if (! torcfg->disable_iocp)
+    if (! torcfg->disable_iocp) {
+      evthread_use_windows_threads();
       event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP);
       event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP);
+    }
 #endif
 #endif
 
 
 #if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER >= V(2,0,7)
 #if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER >= V(2,0,7)

+ 0 - 4
src/common/torgzip.c

@@ -43,11 +43,7 @@
 #define off64_t int64_t
 #define off64_t int64_t
 #endif
 #endif
 
 
-#ifdef _MSC_VER
-#include "..\..\contrib\zlib\zlib.h"
-#else
 #include <zlib.h>
 #include <zlib.h>
-#endif
 
 
 /** Set to 1 if zlib is a version that supports gzip; set to 0 if it doesn't;
 /** Set to 1 if zlib is a version that supports gzip; set to 0 if it doesn't;
  * set to -1 if we haven't checked yet. */
  * set to -1 if we haven't checked yet. */

+ 9 - 0
src/common/torint.h

@@ -111,6 +111,15 @@ typedef signed int int32_t;
 typedef unsigned int uint32_t;
 typedef unsigned int uint32_t;
 #define HAVE_UINT32_T
 #define HAVE_UINT32_T
 #endif
 #endif
+#ifndef UINT16_MAX
+#define UINT16_MAX 0xffffu
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX 0x7fff
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-INT16_MAX-1)
+#endif
 #ifndef UINT32_MAX
 #ifndef UINT32_MAX
 #define UINT32_MAX 0xffffffffu
 #define UINT32_MAX 0xffffffffu
 #endif
 #endif

+ 16 - 0
src/common/tortls.c

@@ -273,6 +273,22 @@ tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
 
 
   addr = tls ? tls->address : NULL;
   addr = tls ? tls->address : NULL;
 
 
+  /* Some errors are known-benign, meaning they are the fault of the other
+   * side of the connection. The caller doesn't know this, so override the
+   * priority for those cases. */
+  switch (ERR_GET_REASON(err)) {
+    case SSL_R_HTTP_REQUEST:
+    case SSL_R_HTTPS_PROXY_REQUEST:
+    case SSL_R_RECORD_LENGTH_MISMATCH:
+    case SSL_R_RECORD_TOO_LARGE:
+    case SSL_R_UNKNOWN_PROTOCOL:
+    case SSL_R_UNSUPPORTED_PROTOCOL:
+      severity = LOG_INFO;
+      break;
+    default:
+      break;
+  }
+
   msg = (const char*)ERR_reason_error_string(err);
   msg = (const char*)ERR_reason_error_string(err);
   lib = (const char*)ERR_lib_error_string(err);
   lib = (const char*)ERR_lib_error_string(err);
   func = (const char*)ERR_func_error_string(err);
   func = (const char*)ERR_func_error_string(err);

+ 72 - 3
src/common/util.c

@@ -14,6 +14,9 @@
 #define _GNU_SOURCE
 #define _GNU_SOURCE
 
 
 #include "orconfig.h"
 #include "orconfig.h"
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
 #define UTIL_PRIVATE
 #define UTIL_PRIVATE
 #include "util.h"
 #include "util.h"
 #include "torlog.h"
 #include "torlog.h"
@@ -68,9 +71,6 @@
 #ifdef HAVE_SYS_FCNTL_H
 #ifdef HAVE_SYS_FCNTL_H
 #include <sys/fcntl.h>
 #include <sys/fcntl.h>
 #endif
 #endif
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
 #ifdef HAVE_TIME_H
 #ifdef HAVE_TIME_H
 #include <time.h>
 #include <time.h>
 #endif
 #endif
@@ -412,6 +412,32 @@ round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor)
   return number;
   return number;
 }
 }
 
 
+/** Return the number of bits set in <b>v</b>. */
+int
+n_bits_set_u8(uint8_t v)
+{
+  static const int nybble_table[] = {
+    0, /* 0000 */
+    1, /* 0001 */
+    1, /* 0010 */
+    2, /* 0011 */
+    1, /* 0100 */
+    2, /* 0101 */
+    2, /* 0110 */
+    3, /* 0111 */
+    1, /* 1000 */
+    2, /* 1001 */
+    2, /* 1010 */
+    3, /* 1011 */
+    2, /* 1100 */
+    3, /* 1101 */
+    3, /* 1110 */
+    4, /* 1111 */
+  };
+
+  return nybble_table[v & 15] + nybble_table[v>>4];
+}
+
 /* =====
 /* =====
  * String manipulation
  * String manipulation
  * ===== */
  * ===== */
@@ -495,6 +521,23 @@ tor_strisnonupper(const char *s)
   return 1;
   return 1;
 }
 }
 
 
+/** As strcmp, except that either string may be NULL.  The NULL string is
+ * considered to be before any non-NULL string. */
+int
+strcmp_opt(const char *s1, const char *s2)
+{
+  if (!s1) {
+    if (!s2)
+      return 0;
+    else
+      return -1;
+  } else if (!s2) {
+    return 1;
+  } else {
+    return strcmp(s1, s2);
+  }
+}
+
 /** Compares the first strlen(s2) characters of s1 with s2.  Returns as for
 /** Compares the first strlen(s2) characters of s1 with s2.  Returns as for
  * strcmp.
  * strcmp.
  */
  */
@@ -1693,6 +1736,8 @@ check_private_dir(const char *dirname, cpd_check_t check,
   struct passwd *pw = NULL;
   struct passwd *pw = NULL;
   uid_t running_uid;
   uid_t running_uid;
   gid_t running_gid;
   gid_t running_gid;
+#else
+  (void)effective_user;
 #endif
 #endif
 
 
   tor_assert(dirname);
   tor_assert(dirname);
@@ -2636,6 +2681,30 @@ tor_sscanf(const char *buf, const char *pattern, ...)
   return r;
   return r;
 }
 }
 
 
+/** Append the string produced by tor_asprintf(<b>pattern</b>, <b>...</b>)
+ * to <b>sl</b>. */
+void
+smartlist_asprintf_add(struct smartlist_t *sl, const char *pattern, ...)
+{
+  va_list ap;
+  va_start(ap, pattern);
+  smartlist_vasprintf_add(sl, pattern, ap);
+  va_end(ap);
+}
+
+/** va_list-based backend of smartlist_asprintf_add. */
+void
+smartlist_vasprintf_add(struct smartlist_t *sl, const char *pattern,
+                        va_list args)
+{
+  char *str = NULL;
+
+  tor_vasprintf(&str, pattern, args);
+  tor_assert(str != NULL);
+
+  smartlist_add(sl, str);
+}
+
 /** Return a new list containing the filenames in the directory <b>dirname</b>.
 /** Return a new list containing the filenames in the directory <b>dirname</b>.
  * Return NULL on error or if <b>dirname</b> is not a directory.
  * Return NULL on error or if <b>dirname</b> is not a directory.
  */
  */

+ 7 - 0
src/common/util.h

@@ -160,6 +160,7 @@ uint64_t round_to_power_of_2(uint64_t u64);
 unsigned round_to_next_multiple_of(unsigned number, unsigned divisor);
 unsigned round_to_next_multiple_of(unsigned number, unsigned divisor);
 uint32_t round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor);
 uint32_t round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor);
 uint64_t round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor);
 uint64_t round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor);
+int n_bits_set_u8(uint8_t v);
 
 
 /* Compute the CEIL of <b>a</b> divided by <b>b</b>, for nonnegative <b>a</b>
 /* Compute the CEIL of <b>a</b> divided by <b>b</b>, for nonnegative <b>a</b>
  * and positive <b>b</b>.  Works on integer types only. Not defined if a+b can
  * and positive <b>b</b>.  Works on integer types only. Not defined if a+b can
@@ -174,6 +175,7 @@ void tor_strlower(char *s) ATTR_NONNULL((1));
 void tor_strupper(char *s) ATTR_NONNULL((1));
 void tor_strupper(char *s) ATTR_NONNULL((1));
 int tor_strisprint(const char *s) ATTR_PURE ATTR_NONNULL((1));
 int tor_strisprint(const char *s) ATTR_PURE ATTR_NONNULL((1));
 int tor_strisnonupper(const char *s) ATTR_PURE ATTR_NONNULL((1));
 int tor_strisnonupper(const char *s) ATTR_PURE ATTR_NONNULL((1));
+int strcmp_opt(const char *s1, const char *s2) ATTR_PURE;
 int strcmpstart(const char *s1, const char *s2) ATTR_PURE ATTR_NONNULL((1,2));
 int strcmpstart(const char *s1, const char *s2) ATTR_PURE ATTR_NONNULL((1,2));
 int strcmp_len(const char *s1, const char *s2, size_t len)
 int strcmp_len(const char *s1, const char *s2, size_t len)
   ATTR_PURE ATTR_NONNULL((1,2));
   ATTR_PURE ATTR_NONNULL((1,2));
@@ -218,6 +220,11 @@ int tor_sscanf(const char *buf, const char *pattern, ...)
 #endif
 #endif
   ;
   ;
 
 
+void smartlist_asprintf_add(struct smartlist_t *sl, const char *pattern, ...)
+  CHECK_PRINTF(2, 3);
+void smartlist_vasprintf_add(struct smartlist_t *sl, const char *pattern,
+                             va_list args);
+
 int hex_decode_digit(char c);
 int hex_decode_digit(char c);
 void base16_encode(char *dest, size_t destlen, const char *src, size_t srclen);
 void base16_encode(char *dest, size_t destlen, const char *src, size_t srclen);
 int base16_decode(char *dest, size_t destlen, const char *src, size_t srclen);
 int base16_decode(char *dest, size_t destlen, const char *src, size_t srclen);

File diff suppressed because it is too large
+ 466 - 107
src/config/geoip


+ 1 - 1
src/or/Makefile.am

@@ -7,7 +7,7 @@ else
 tor_platform_source=
 tor_platform_source=
 endif
 endif
 
 
-EXTRA_DIST=ntmain.c or_sha1.i
+EXTRA_DIST=ntmain.c or_sha1.i Makefile.nmake
 
 
 if USE_EXTERNAL_EVDNS
 if USE_EXTERNAL_EVDNS
 evdns_source=
 evdns_source=

+ 28 - 0
src/or/Makefile.nmake

@@ -0,0 +1,28 @@
+all: tor.exe
+
+CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include /I ..\common
+
+LIBS = ..\..\..\build-alpha\lib\libevent.a \
+ ..\..\..\build-alpha\lib\libcrypto.a \
+ ..\..\..\build-alpha\lib\libssl.a \
+ ..\..\..\build-alpha\lib\libz.a \
+ ws2_32.lib advapi32.lib shell32.lib
+
+LIBTOR_OBJECTS = buffers.obj circuitbuild.obj circuitlist.obj circuituse.obj \
+	command.obj config.obj connection.obj connection_edge.obj \
+	connection_or.obj control.obj cpuworker.obj directory.obj \
+	dirserv.obj dirvote.obj dns.obj dnsserv.obj geoip.obj \
+	hibernate.obj main.obj microdesc.obj networkstatus.obj \
+	nodelist.obj onion.obj policies.obj reasons.obj relay.obj \
+	rendclient.obj rendcommon.obj rendmid.obj rendservice.obj \
+	rephist.obj router.obj routerlist.obj routerparse.obj status.obj \
+	config_codedigest.obj ntmain.obj
+
+libtor.lib: $(LIBTOR_OBJECTS)
+	lib $(LIBTOR_OBJECTS) /out:libtor.lib
+
+tor.exe: libtor.lib tor_main.obj
+	$(CC) $(CFLAGS) $(LIBS) libtor.lib ..\common\*.lib tor_main.obj
+
+clean:
+	del $(LIBTOR_OBJECTS) *.lib tor.exe

+ 88 - 22
src/or/buffers.c

@@ -557,6 +557,39 @@ buf_free(buf_t *buf)
   tor_free(buf);
   tor_free(buf);
 }
 }
 
 
+/** Return a new copy of <b>in_chunk</b> */
+static chunk_t *
+chunk_copy(const chunk_t *in_chunk)
+{
+  chunk_t *newch = tor_memdup(in_chunk, CHUNK_ALLOC_SIZE(in_chunk->memlen));
+  newch->next = NULL;
+  if (in_chunk->data) {
+    off_t offset = in_chunk->data - in_chunk->mem;
+    newch->data = newch->mem + offset;
+  }
+  return newch;
+}
+
+/** Return a new copy of <b>buf</b> */
+buf_t *
+buf_copy(const buf_t *buf)
+{
+  chunk_t *ch;
+  buf_t *out = buf_new();
+  out->default_chunk_size = buf->default_chunk_size;
+  for (ch = buf->head; ch; ch = ch->next) {
+    chunk_t *newch = chunk_copy(ch);
+    if (out->tail) {
+      out->tail->next = newch;
+      out->tail = newch;
+    } else {
+      out->head = out->tail = newch;
+    }
+  }
+  out->datalen = buf->datalen;
+  return out;
+}
+
 /** Append a new chunk with enough capacity to hold <b>capacity</b> bytes to
 /** Append a new chunk with enough capacity to hold <b>capacity</b> bytes to
  * the tail of <b>buf</b>.  If <b>capped</b>, don't allocate a chunk bigger
  * the tail of <b>buf</b>.  If <b>capped</b>, don't allocate a chunk bigger
  * than MAX_CHUNK_ALLOC. */
  * than MAX_CHUNK_ALLOC. */
@@ -1021,13 +1054,13 @@ fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto)
 #ifdef USE_BUFFEREVENTS
 #ifdef USE_BUFFEREVENTS
 /** Try to read <b>n</b> bytes from <b>buf</b> at <b>pos</b> (which may be
 /** Try to read <b>n</b> bytes from <b>buf</b> at <b>pos</b> (which may be
  * NULL for the start of the buffer), copying the data only if necessary.  Set
  * NULL for the start of the buffer), copying the data only if necessary.  Set
- * *<b>data</b> to a pointer to the desired bytes.  Set <b>free_out</b> to 1
+ * *<b>data_out</b> to a pointer to the desired bytes.  Set <b>free_out</b> to 1
  * if we needed to malloc *<b>data</b> because the original bytes were
  * if we needed to malloc *<b>data</b> because the original bytes were
  * noncontiguous; 0 otherwise.  Return the number of bytes actually available
  * noncontiguous; 0 otherwise.  Return the number of bytes actually available
- * at <b>data</b>.
+ * at *<b>data_out</b>.
  */
  */
 static ssize_t
 static ssize_t
-inspect_evbuffer(struct evbuffer *buf, char **data, size_t n, int *free_out,
+inspect_evbuffer(struct evbuffer *buf, char **data_out, size_t n, int *free_out,
                  struct evbuffer_ptr *pos)
                  struct evbuffer_ptr *pos)
 {
 {
   int n_vecs, i;
   int n_vecs, i;
@@ -1042,25 +1075,15 @@ inspect_evbuffer(struct evbuffer *buf, char **data, size_t n, int *free_out,
     struct evbuffer_iovec v;
     struct evbuffer_iovec v;
     i = evbuffer_peek(buf, n, pos, &v, 1);
     i = evbuffer_peek(buf, n, pos, &v, 1);
     tor_assert(i == 1);
     tor_assert(i == 1);
-    *data = v.iov_base;
+    *data_out = v.iov_base;
     *free_out = 0;
     *free_out = 0;
     return v.iov_len;
     return v.iov_len;
   } else {
   } else {
-    struct evbuffer_iovec *vecs =
-      tor_malloc(sizeof(struct evbuffer_iovec)*n_vecs);
-    size_t copied = 0;
-    i = evbuffer_peek(buf, n, NULL, vecs, n_vecs);
-    tor_assert(i == n_vecs);
-    *data = tor_malloc(n);
-    for (i=0; i < n_vecs; ++i) {
-      size_t copy = n - copied;
-      if (copy > vecs[i].iov_len)
-        copy = vecs[i].iov_len;
-      tor_assert(copied+copy <= n);
-      memcpy(data+copied, vecs[i].iov_base, copy);
-      copied += copy;
-    }
+    ev_ssize_t copied;
+    *data_out = tor_malloc(n);
     *free_out = 1;
     *free_out = 1;
+    copied = evbuffer_copyout(buf, *data_out, n);
+    tor_assert(copied >= 0 && (size_t)copied == n);
     return copied;
     return copied;
   }
   }
 }
 }
@@ -1499,8 +1522,14 @@ socks_request_free(socks_request_t *req)
 {
 {
   if (!req)
   if (!req)
     return;
     return;
-  tor_free(req->username);
-  tor_free(req->password);
+  if (req->username) {
+    memset(req->username, 0x10, req->usernamelen);
+    tor_free(req->username);
+  }
+  if (req->password) {
+    memset(req->password, 0x04, req->passwordlen);
+    tor_free(req->password);
+  }
   memset(req, 0xCC, sizeof(socks_request_t));
   memset(req, 0xCC, sizeof(socks_request_t));
   tor_free(req);
   tor_free(req);
 }
 }
@@ -1581,12 +1610,12 @@ fetch_from_evbuffer_socks(struct evbuffer *buf, socks_request_t *req,
      */
      */
     struct evbuffer_iovec v;
     struct evbuffer_iovec v;
     int i;
     int i;
-    want_length = evbuffer_get_contiguous_space(buf);
     n_drain = 0;
     n_drain = 0;
-    i = evbuffer_peek(buf, want_length, NULL, &v, 1);
+    i = evbuffer_peek(buf, -1, NULL, &v, 1);
     tor_assert(i == 1);
     tor_assert(i == 1);
     data = v.iov_base;
     data = v.iov_base;
     datalen = v.iov_len;
     datalen = v.iov_len;
+    want_length = 0;
 
 
     res = parse_socks(data, datalen, req, log_sockstype,
     res = parse_socks(data, datalen, req, log_sockstype,
                       safe_socks, &n_drain, &want_length);
                       safe_socks, &n_drain, &want_length);
@@ -2374,6 +2403,43 @@ write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state,
 }
 }
 #endif
 #endif
 
 
+/** Set *<b>output</b> to contain a copy of the data in *<b>input</b> */
+int
+generic_buffer_set_to_copy(generic_buffer_t **output,
+                           const generic_buffer_t *input)
+{
+#ifdef USE_BUFFEREVENTS
+  struct evbuffer_ptr ptr;
+  size_t remaining = evbuffer_get_length(input);
+  if (*output) {
+    evbuffer_drain(*output, evbuffer_get_length(*output));
+  } else {
+    if (!(*output = evbuffer_new()))
+      return -1;
+  }
+  evbuffer_ptr_set((struct evbuffer*)input, &ptr, 0, EVBUFFER_PTR_SET);
+  while (remaining) {
+    struct evbuffer_iovec v[4];
+    int n_used, i;
+    n_used = evbuffer_peek((struct evbuffer*)input, -1, &ptr, v, 4);
+    if (n_used < 0)
+      return -1;
+    for (i=0;i<n_used;++i) {
+      evbuffer_add(*output, v[i].iov_base, v[i].iov_len);
+      tor_assert(v[i].iov_len <= remaining);
+      remaining -= v[i].iov_len;
+      evbuffer_ptr_set((struct evbuffer*)input,
+                       &ptr, v[i].iov_len, EVBUFFER_PTR_ADD);
+    }
+  }
+#else
+  if (*output)
+    buf_free(*output);
+  *output = buf_copy(input);
+#endif
+  return 0;
+}
+
 /** Log an error and exit if <b>buf</b> is corrupted.
 /** Log an error and exit if <b>buf</b> is corrupted.
  */
  */
 void
 void

+ 19 - 0
src/or/buffers.h

@@ -16,6 +16,7 @@ buf_t *buf_new(void);
 buf_t *buf_new_with_capacity(size_t size);
 buf_t *buf_new_with_capacity(size_t size);
 void buf_free(buf_t *buf);
 void buf_free(buf_t *buf);
 void buf_clear(buf_t *buf);
 void buf_clear(buf_t *buf);
+buf_t *buf_copy(const buf_t *buf);
 void buf_shrink(buf_t *buf);
 void buf_shrink(buf_t *buf);
 void buf_shrink_freelists(int free_all);
 void buf_shrink_freelists(int free_all);
 void buf_dump_freelist_sizes(int severity);
 void buf_dump_freelist_sizes(int severity);
@@ -67,6 +68,24 @@ int write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state,
                            int done);
                            int done);
 #endif
 #endif
 
 
+#ifdef USE_BUFFEREVENTS
+#define generic_buffer_new() evbuffer_new()
+#define generic_buffer_len(b) evbuffer_get_length((b))
+#define generic_buffer_add(b,dat,len) evbuffer_add((b),(dat),(len))
+#define generic_buffer_get(b,buf,buflen) evbuffer_remove((b),(buf),(buflen))
+#define generic_buffer_clear(b) evbuffer_drain((b), evbuffer_get_length((b)))
+#define generic_buffer_free(b) evbuffer_free((b))
+#else
+#define generic_buffer_new() buf_new()
+#define generic_buffer_len(b) buf_datalen((b))
+#define generic_buffer_add(b,dat,len) write_to_buf((dat),(len),(b))
+#define generic_buffer_get(b,buf,buflen) fetch_from_buf((buf),(buflen),(b))
+#define generic_buffer_clear(b) buf_clear((b))
+#define generic_buffer_free(b) buf_free((b))
+#endif
+int generic_buffer_set_to_copy(generic_buffer_t **output,
+                               const generic_buffer_t *input);
+
 void assert_buf_ok(buf_t *buf);
 void assert_buf_ok(buf_t *buf);
 
 
 #ifdef BUFFERS_PRIVATE
 #ifdef BUFFERS_PRIVATE

+ 12 - 1
src/or/circuitlist.c

@@ -550,6 +550,16 @@ circuit_free(circuit_t *circ)
 
 
     crypto_free_pk_env(ocirc->intro_key);
     crypto_free_pk_env(ocirc->intro_key);
     rend_data_free(ocirc->rend_data);
     rend_data_free(ocirc->rend_data);
+
+    tor_free(ocirc->dest_address);
+    if (ocirc->socks_username) {
+      memset(ocirc->socks_username, 0x12, ocirc->socks_username_len);
+      tor_free(ocirc->socks_username);
+    }
+    if (ocirc->socks_password) {
+      memset(ocirc->socks_password, 0x06, ocirc->socks_password_len);
+      tor_free(ocirc->socks_password);
+    }
   } else {
   } else {
     or_circuit_t *ocirc = TO_OR_CIRCUIT(circ);
     or_circuit_t *ocirc = TO_OR_CIRCUIT(circ);
     /* Remember cell statistics for this circuit before deallocating. */
     /* Remember cell statistics for this circuit before deallocating. */
@@ -1001,7 +1011,8 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
           (!need_capacity || circ->build_state->need_capacity) &&
           (!need_capacity || circ->build_state->need_capacity) &&
           (internal == circ->build_state->is_internal) &&
           (internal == circ->build_state->is_internal) &&
           circ->remaining_relay_early_cells &&
           circ->remaining_relay_early_cells &&
-          !circ->build_state->onehop_tunnel) {
+          !circ->build_state->onehop_tunnel &&
+          !circ->isolation_values_set) {
         if (info) {
         if (info) {
           /* need to make sure we don't duplicate hops */
           /* need to make sure we don't duplicate hops */
           crypt_path_t *hop = circ->cpath;
           crypt_path_t *hop = circ->cpath;

+ 170 - 22
src/or/circuituse.c

@@ -18,6 +18,7 @@
 #include "connection_edge.h"
 #include "connection_edge.h"
 #include "control.h"
 #include "control.h"
 #include "nodelist.h"
 #include "nodelist.h"
+#include "networkstatus.h"
 #include "policies.h"
 #include "policies.h"
 #include "rendclient.h"
 #include "rendclient.h"
 #include "rendcommon.h"
 #include "rendcommon.h"
@@ -39,19 +40,19 @@ static void circuit_increment_failure_count(void);
  * Else return 0.
  * Else return 0.
  */
  */
 static int
 static int
-circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
+circuit_is_acceptable(const origin_circuit_t *origin_circ,
+                      const edge_connection_t *conn,
                       int must_be_open, uint8_t purpose,
                       int must_be_open, uint8_t purpose,
                       int need_uptime, int need_internal,
                       int need_uptime, int need_internal,
                       time_t now)
                       time_t now)
 {
 {
+  const circuit_t *circ = TO_CIRCUIT(origin_circ);
   const node_t *exitnode;
   const node_t *exitnode;
   cpath_build_state_t *build_state;
   cpath_build_state_t *build_state;
   tor_assert(circ);
   tor_assert(circ);
   tor_assert(conn);
   tor_assert(conn);
   tor_assert(conn->socks_request);
   tor_assert(conn->socks_request);
 
 
-  if (!CIRCUIT_IS_ORIGIN(circ))
-    return 0; /* this circ doesn't start at us */
   if (must_be_open && (circ->state != CIRCUIT_STATE_OPEN || !circ->n_conn))
   if (must_be_open && (circ->state != CIRCUIT_STATE_OPEN || !circ->n_conn))
     return 0; /* ignore non-open circs */
     return 0; /* ignore non-open circs */
   if (circ->marked_for_close)
   if (circ->marked_for_close)
@@ -86,7 +87,7 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
    * circuit, it's the magical extra bob hop. so just check the nickname
    * circuit, it's the magical extra bob hop. so just check the nickname
    * of the one we meant to finish at.
    * of the one we meant to finish at.
    */
    */
-  build_state = TO_ORIGIN_CIRCUIT(circ)->build_state;
+  build_state = origin_circ->build_state;
   exitnode = build_state_get_exit_node(build_state);
   exitnode = build_state_get_exit_node(build_state);
 
 
   if (need_uptime && !build_state->need_uptime)
   if (need_uptime && !build_state->need_uptime)
@@ -134,25 +135,37 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
       return 0;
       return 0;
     }
     }
   } else { /* not general */
   } else { /* not general */
-    origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
-    if ((conn->rend_data && !ocirc->rend_data) ||
-        (!conn->rend_data && ocirc->rend_data) ||
-        (conn->rend_data && ocirc->rend_data &&
+    if ((conn->rend_data && !origin_circ->rend_data) ||
+        (!conn->rend_data && origin_circ->rend_data) ||
+        (conn->rend_data && origin_circ->rend_data &&
          rend_cmp_service_ids(conn->rend_data->onion_address,
          rend_cmp_service_ids(conn->rend_data->onion_address,
-                              ocirc->rend_data->onion_address))) {
+                              origin_circ->rend_data->onion_address))) {
       /* this circ is not for this conn */
       /* this circ is not for this conn */
       return 0;
       return 0;
     }
     }
   }
   }
+
+  if (!connection_edge_compatible_with_circuit(conn, origin_circ)) {
+    /* conn needs to be isolated from other conns that have already used
+     * origin_circ */
+    return 0;
+  }
+
   return 1;
   return 1;
 }
 }
 
 
 /** Return 1 if circuit <b>a</b> is better than circuit <b>b</b> for
 /** Return 1 if circuit <b>a</b> is better than circuit <b>b</b> for
- * <b>purpose</b>, and return 0 otherwise. Used by circuit_get_best.
+ * <b>conn</b>, and return 0 otherwise. Used by circuit_get_best.
  */
  */
 static int
 static int
-circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
+circuit_is_better(const origin_circuit_t *oa, const origin_circuit_t *ob,
+                  const edge_connection_t *conn)
 {
 {
+  const circuit_t *a = TO_CIRCUIT(oa);
+  const circuit_t *b = TO_CIRCUIT(ob);
+  const uint8_t purpose = conn->_base.purpose;
+  int a_bits, b_bits;
+
   switch (purpose) {
   switch (purpose) {
     case CIRCUIT_PURPOSE_C_GENERAL:
     case CIRCUIT_PURPOSE_C_GENERAL:
       /* if it's used but less dirty it's best;
       /* if it's used but less dirty it's best;
@@ -166,8 +179,7 @@ circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
         if (a->timestamp_dirty ||
         if (a->timestamp_dirty ||
             timercmp(&a->timestamp_created, &b->timestamp_created, >))
             timercmp(&a->timestamp_created, &b->timestamp_created, >))
           return 1;
           return 1;
-        if (CIRCUIT_IS_ORIGIN(b) &&
-            TO_ORIGIN_CIRCUIT(b)->build_state->is_internal)
+        if (ob->build_state->is_internal)
           /* XXX023 what the heck is this internal thing doing here. I
           /* XXX023 what the heck is this internal thing doing here. I
            * think we can get rid of it. circuit_is_acceptable() already
            * think we can get rid of it. circuit_is_acceptable() already
            * makes sure that is_internal is exactly what we need it to
            * makes sure that is_internal is exactly what we need it to
@@ -186,6 +198,29 @@ circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
         return 1;
         return 1;
       break;
       break;
   }
   }
+
+  /* XXXX023 Maybe this check should get a higher priority to avoid
+   *   using up circuits too rapidly. */
+
+  a_bits = connection_edge_update_circuit_isolation(conn,
+                                                    (origin_circuit_t*)oa, 1);
+  b_bits = connection_edge_update_circuit_isolation(conn,
+                                                    (origin_circuit_t*)ob, 1);
+  /* if x_bits < 0, then we have not used x for anything; better not to dirty
+   * a connection if we can help it. */
+  if (a_bits < 0) {
+    return 0;
+  } else if (b_bits < 0) {
+    return 1;
+  }
+  a_bits &= ~ oa->isolation_flags_mixed;
+  a_bits &= ~ ob->isolation_flags_mixed;
+  if (n_bits_set_u8(a_bits) < n_bits_set_u8(b_bits)) {
+    /* The fewer new restrictions we need to make on a circuit for stream
+     * isolation, the better. */
+    return 1;
+  }
+
   return 0;
   return 0;
 }
 }
 
 
@@ -206,10 +241,12 @@ circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
  * closest introduce-purposed circuit that you can find.
  * closest introduce-purposed circuit that you can find.
  */
  */
 static origin_circuit_t *
 static origin_circuit_t *
-circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose,
+circuit_get_best(const edge_connection_t *conn,
+                 int must_be_open, uint8_t purpose,
                  int need_uptime, int need_internal)
                  int need_uptime, int need_internal)
 {
 {
-  circuit_t *circ, *best=NULL;
+  circuit_t *circ;
+  origin_circuit_t *best=NULL;
   struct timeval now;
   struct timeval now;
   int intro_going_on_but_too_old = 0;
   int intro_going_on_but_too_old = 0;
 
 
@@ -222,7 +259,11 @@ circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose,
   tor_gettimeofday(&now);
   tor_gettimeofday(&now);
 
 
   for (circ=global_circuitlist;circ;circ = circ->next) {
   for (circ=global_circuitlist;circ;circ = circ->next) {
-    if (!circuit_is_acceptable(circ,conn,must_be_open,purpose,
+    origin_circuit_t *origin_circ;
+    if (!CIRCUIT_IS_ORIGIN(circ))
+      continue;
+    origin_circ = TO_ORIGIN_CIRCUIT(circ);
+    if (!circuit_is_acceptable(origin_circ,conn,must_be_open,purpose,
                                need_uptime,need_internal,now.tv_sec))
                                need_uptime,need_internal,now.tv_sec))
       continue;
       continue;
 
 
@@ -236,8 +277,8 @@ circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose,
     /* now this is an acceptable circ to hand back. but that doesn't
     /* now this is an acceptable circ to hand back. but that doesn't
      * mean it's the *best* circ to hand back. try to decide.
      * mean it's the *best* circ to hand back. try to decide.
      */
      */
-    if (!best || circuit_is_better(circ,best,purpose))
-      best = circ;
+    if (!best || circuit_is_better(origin_circ,best,conn))
+      best = origin_circ;
   }
   }
 
 
   if (!best && intro_going_on_but_too_old)
   if (!best && intro_going_on_but_too_old)
@@ -245,7 +286,28 @@ circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose,
              "right now, but it has already taken quite a while. Starting "
              "right now, but it has already taken quite a while. Starting "
              "one in parallel.");
              "one in parallel.");
 
 
-  return best ? TO_ORIGIN_CIRCUIT(best) : NULL;
+  return best;
+}
+
+/** Return the number of not-yet-open general-purpose origin circuits. */
+static int
+count_pending_general_client_circuits(void)
+{
+  const circuit_t *circ;
+
+  int count = 0;
+
+  for (circ = global_circuitlist; circ; circ = circ->next) {
+    if (circ->marked_for_close ||
+        circ->state == CIRCUIT_STATE_OPEN ||
+        circ->purpose != CIRCUIT_PURPOSE_C_GENERAL ||
+        !CIRCUIT_IS_ORIGIN(circ))
+      continue;
+
+    ++count;
+  }
+
+  return count;
 }
 }
 
 
 #if 0
 #if 0
@@ -677,6 +739,7 @@ circuit_detach_stream(circuit_t *circ, edge_connection_t *conn)
   tor_assert(conn);
   tor_assert(conn);
 
 
   conn->cpath_layer = NULL; /* make sure we don't keep a stale pointer */
   conn->cpath_layer = NULL; /* make sure we don't keep a stale pointer */
+  conn->may_use_optimistic_data = 0;
   conn->on_circuit = NULL;
   conn->on_circuit = NULL;
 
 
   if (CIRCUIT_IS_ORIGIN(circ)) {
   if (CIRCUIT_IS_ORIGIN(circ)) {
@@ -937,6 +1000,7 @@ circuit_testing_failed(origin_circuit_t *circ, int at_last_hop)
 void
 void
 circuit_has_opened(origin_circuit_t *circ)
 circuit_has_opened(origin_circuit_t *circ)
 {
 {
+  int can_try_clearing_isolation = 0, tried_clearing_isolation = 0;
   control_event_circuit_status(circ, CIRC_EVENT_BUILT, 0);
   control_event_circuit_status(circ, CIRC_EVENT_BUILT, 0);
 
 
   /* Remember that this circuit has finished building. Now if we start
   /* Remember that this circuit has finished building. Now if we start
@@ -944,9 +1008,12 @@ circuit_has_opened(origin_circuit_t *circ)
    * to consider its build time. */
    * to consider its build time. */
   circ->has_opened = 1;
   circ->has_opened = 1;
 
 
+ again:
+
   switch (TO_CIRCUIT(circ)->purpose) {
   switch (TO_CIRCUIT(circ)->purpose) {
     case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
     case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
       rend_client_rendcirc_has_opened(circ);
       rend_client_rendcirc_has_opened(circ);
+      can_try_clearing_isolation = 1;
       connection_ap_attach_pending();
       connection_ap_attach_pending();
       break;
       break;
     case CIRCUIT_PURPOSE_C_INTRODUCING:
     case CIRCUIT_PURPOSE_C_INTRODUCING:
@@ -955,6 +1022,7 @@ circuit_has_opened(origin_circuit_t *circ)
     case CIRCUIT_PURPOSE_C_GENERAL:
     case CIRCUIT_PURPOSE_C_GENERAL:
       /* Tell any AP connections that have been waiting for a new
       /* Tell any AP connections that have been waiting for a new
        * circuit that one is ready. */
        * circuit that one is ready. */
+      can_try_clearing_isolation = 1;
       connection_ap_attach_pending();
       connection_ap_attach_pending();
       break;
       break;
     case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
     case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
@@ -972,6 +1040,24 @@ circuit_has_opened(origin_circuit_t *circ)
      * This won't happen in normal operation, but might happen if the
      * This won't happen in normal operation, but might happen if the
      * controller did it. Just let it slide. */
      * controller did it. Just let it slide. */
   }
   }
+
+  if (/* The circuit may have become non-open if it was cannibalized.*/
+      circ->_base.state == CIRCUIT_STATE_OPEN &&
+      /* Only if the purpose is clearable, and only if we haven't tried
+       * to clear isolation yet, do we try. */
+      can_try_clearing_isolation && !tried_clearing_isolation &&
+      /* If !isolation_values_set, there is nothing to clear. */
+      circ->isolation_values_set &&
+      /* It's not legal to clear a circuit's isolation info if it's ever had
+       * streams attached */
+      !circ->isolation_any_streams_attached) {
+    /* If we have any isolation information set on this circuit, and
+     * we didn't manage to attach any streams to it, then we can
+     * and should clear it and try again. */
+    circuit_clear_isolation(circ);
+    tried_clearing_isolation = 1;
+    goto again;
+  }
 }
 }
 
 
 /** Called whenever a circuit could not be successfully built.
 /** Called whenever a circuit could not be successfully built.
@@ -1307,6 +1393,20 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
   if (!circ) {
   if (!circ) {
     extend_info_t *extend_info=NULL;
     extend_info_t *extend_info=NULL;
     uint8_t new_circ_purpose;
     uint8_t new_circ_purpose;
+    const int n_pending = count_pending_general_client_circuits();
+
+    if (n_pending >= options->MaxClientCircuitsPending) {
+      static ratelim_t delay_limit = RATELIM_INIT(10*60);
+      char *m;
+      if ((m = rate_limit_log(&delay_limit, approx_time()))) {
+        log_notice(LD_APP, "We'd like to launch a circuit to handle a "
+                   "connection, but we already have %d general-purpose client "
+                   "circuits pending. Waiting until some finish.",
+                   n_pending);
+        tor_free(m);
+      }
+      return 0;
+    }
 
 
     if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
     if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
       /* need to pick an intro point */
       /* need to pick an intro point */
@@ -1417,12 +1517,20 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
           rend_client_rendcirc_has_opened(circ);
           rend_client_rendcirc_has_opened(circ);
       }
       }
     }
     }
-  }
-  if (!circ)
+  } /* endif (!circ) */
+  if (circ) {
+    /* Mark the circuit with the isolation fields for this connection.
+     * When the circuit arrives, we'll clear these flags: this is
+     * just some internal bookkeeping to make sure that we have
+     * launched enough circuits.
+     */
+    connection_edge_update_circuit_isolation(conn, circ, 0);
+  } else {
     log_info(LD_APP,
     log_info(LD_APP,
              "No safe circuit (purpose %d) ready for edge "
              "No safe circuit (purpose %d) ready for edge "
              "connection; delaying.",
              "connection; delaying.",
              desired_circuit_purpose);
              desired_circuit_purpose);
+  }
   *circp = circ;
   *circp = circ;
   return 0;
   return 0;
 }
 }
@@ -1441,6 +1549,21 @@ cpath_is_on_circuit(origin_circuit_t *circ, crypt_path_t *crypt_path)
   return 0;
   return 0;
 }
 }
 
 
+/** Return true iff client-side optimistic data is supported. */
+static int
+optimistic_data_enabled(void)
+{
+  const or_options_t *options = get_options();
+  if (options->OptimisticData < 0) {
+    /* XXX023 consider having auto default to 1 rather than 0 before
+     * the 0.2.3 branch goes stable. See bug 3617. -RD */
+    const int32_t enabled =
+      networkstatus_get_param(NULL, "UseOptimisticData", 0, 0, 1);
+    return (int)enabled;
+  }
+  return options->OptimisticData;
+}
+
 /** Attach the AP stream <b>apconn</b> to circ's linked list of
 /** Attach the AP stream <b>apconn</b> to circ's linked list of
  * p_streams. Also set apconn's cpath_layer to <b>cpath</b>, or to the last
  * p_streams. Also set apconn's cpath_layer to <b>cpath</b>, or to the last
  * hop in circ's cpath if <b>cpath</b> is NULL.
  * hop in circ's cpath if <b>cpath</b> is NULL.
@@ -1449,6 +1572,8 @@ static void
 link_apconn_to_circ(edge_connection_t *apconn, origin_circuit_t *circ,
 link_apconn_to_circ(edge_connection_t *apconn, origin_circuit_t *circ,
                     crypt_path_t *cpath)
                     crypt_path_t *cpath)
 {
 {
+  const node_t *exitnode;
+
   /* add it into the linked list of streams on this circuit */
   /* add it into the linked list of streams on this circuit */
   log_debug(LD_APP|LD_CIRC, "attaching new conn to circ. n_circ_id %d.",
   log_debug(LD_APP|LD_CIRC, "attaching new conn to circ. n_circ_id %d.",
             circ->_base.n_circ_id);
             circ->_base.n_circ_id);
@@ -1468,6 +1593,28 @@ link_apconn_to_circ(edge_connection_t *apconn, origin_circuit_t *circ,
     tor_assert(circ->cpath->prev->state == CPATH_STATE_OPEN);
     tor_assert(circ->cpath->prev->state == CPATH_STATE_OPEN);
     apconn->cpath_layer = circ->cpath->prev;
     apconn->cpath_layer = circ->cpath->prev;
   }
   }
+
+  circ->isolation_any_streams_attached = 1;
+  connection_edge_update_circuit_isolation(apconn, circ, 0);
+
+  /* See if we can use optimistic data on this circuit */
+  if (apconn->cpath_layer->extend_info &&
+      (exitnode = node_get_by_id(
+                     apconn->cpath_layer->extend_info->identity_digest)) &&
+      exitnode->rs) {
+    /* Okay; we know what exit node this is. */
+    if (optimistic_data_enabled() &&
+        circ->_base.purpose == CIRCUIT_PURPOSE_C_GENERAL &&
+        exitnode->rs->version_supports_optimistic_data)
+      apconn->may_use_optimistic_data = 1;
+    else
+      apconn->may_use_optimistic_data = 0;
+    log_info(LD_APP, "Looks like completed circuit to %s %s allow "
+             "optimistic data for connection to %s",
+             safe_str_client(node_describe(exitnode)),
+             apconn->may_use_optimistic_data ? "does" : "doesn't",
+             safe_str_client(apconn->socks_request->address));
+  }
 }
 }
 
 
 /** Return true iff <b>address</b> is matched by one of the entries in
 /** Return true iff <b>address</b> is matched by one of the entries in
@@ -1495,7 +1642,8 @@ hostname_in_track_host_exits(const or_options_t *options, const char *address)
  * <b>conn</b>'s destination.
  * <b>conn</b>'s destination.
  */
  */
 static void
 static void
-consider_recording_trackhost(edge_connection_t *conn, origin_circuit_t *circ)
+consider_recording_trackhost(const edge_connection_t *conn,
+                             const origin_circuit_t *circ)
 {
 {
   const or_options_t *options = get_options();
   const or_options_t *options = get_options();
   char *new_address = NULL;
   char *new_address = NULL;

+ 439 - 67
src/or/config.c

@@ -40,6 +40,9 @@
 
 
 #include "procmon.h"
 #include "procmon.h"
 
 
+/* From main.c */
+extern int quiet_level;
+
 /** Enumeration of types which option values can take */
 /** Enumeration of types which option values can take */
 typedef enum config_type_t {
 typedef enum config_type_t {
   CONFIG_TYPE_STRING = 0,   /**< An arbitrary string. */
   CONFIG_TYPE_STRING = 0,   /**< An arbitrary string. */
@@ -240,7 +243,7 @@ static config_var_t _option_vars[] = {
   VAR("DirServer",               LINELIST, DirServers, NULL),
   VAR("DirServer",               LINELIST, DirServers, NULL),
   V(DisableAllSwap,              BOOL,     "0"),
   V(DisableAllSwap,              BOOL,     "0"),
   V(DisableIOCP,                 BOOL,     "1"),
   V(DisableIOCP,                 BOOL,     "1"),
-  V(DNSPort,                     PORT,     "0"),
+  V(DNSPort,                     LINELIST, NULL),
   V(DNSListenAddress,            LINELIST, NULL),
   V(DNSListenAddress,            LINELIST, NULL),
   V(DownloadExtraInfo,           BOOL,     "0"),
   V(DownloadExtraInfo,           BOOL,     "0"),
   V(EnforceDistinctSubnets,      BOOL,     "1"),
   V(EnforceDistinctSubnets,      BOOL,     "1"),
@@ -315,13 +318,14 @@ static config_var_t _option_vars[] = {
   VAR("MapAddress",              LINELIST, AddressMap,           NULL),
   VAR("MapAddress",              LINELIST, AddressMap,           NULL),
   V(MaxAdvertisedBandwidth,      MEMUNIT,  "1 GB"),
   V(MaxAdvertisedBandwidth,      MEMUNIT,  "1 GB"),
   V(MaxCircuitDirtiness,         INTERVAL, "10 minutes"),
   V(MaxCircuitDirtiness,         INTERVAL, "10 minutes"),
+  V(MaxClientCircuitsPending,    UINT,     "32"),
   V(MaxOnionsPending,            UINT,     "100"),
   V(MaxOnionsPending,            UINT,     "100"),
   OBSOLETE("MonthlyAccountingStart"),
   OBSOLETE("MonthlyAccountingStart"),
   V(MyFamily,                    STRING,   NULL),
   V(MyFamily,                    STRING,   NULL),
   V(NewCircuitPeriod,            INTERVAL, "30 seconds"),
   V(NewCircuitPeriod,            INTERVAL, "30 seconds"),
   VAR("NamingAuthoritativeDirectory",BOOL, NamingAuthoritativeDir, "0"),
   VAR("NamingAuthoritativeDirectory",BOOL, NamingAuthoritativeDir, "0"),
   V(NATDListenAddress,           LINELIST, NULL),
   V(NATDListenAddress,           LINELIST, NULL),
-  V(NATDPort,                    PORT,     "0"),
+  V(NATDPort,                    LINELIST, NULL),
   V(Nickname,                    STRING,   NULL),
   V(Nickname,                    STRING,   NULL),
   V(WarnUnsafeSocks,              BOOL,     "1"),
   V(WarnUnsafeSocks,              BOOL,     "1"),
   OBSOLETE("NoPublish"),
   OBSOLETE("NoPublish"),
@@ -336,6 +340,7 @@ static config_var_t _option_vars[] = {
   V(PerConnBWRate,               MEMUNIT,  "0"),
   V(PerConnBWRate,               MEMUNIT,  "0"),
   V(PidFile,                     STRING,   NULL),
   V(PidFile,                     STRING,   NULL),
   V(TestingTorNetwork,           BOOL,     "0"),
   V(TestingTorNetwork,           BOOL,     "0"),
+  V(OptimisticData,              AUTOBOOL, "auto"),
   V(PortForwarding,              BOOL,     "0"),
   V(PortForwarding,              BOOL,     "0"),
   V(PortForwardingHelper,        FILENAME, "tor-fw-helper"),
   V(PortForwardingHelper,        FILENAME, "tor-fw-helper"),
   V(PreferTunneledDirConns,      BOOL,     "1"),
   V(PreferTunneledDirConns,      BOOL,     "1"),
@@ -374,7 +379,7 @@ static config_var_t _option_vars[] = {
   V(ShutdownWaitLength,          INTERVAL, "30 seconds"),
   V(ShutdownWaitLength,          INTERVAL, "30 seconds"),
   V(SocksListenAddress,          LINELIST, NULL),
   V(SocksListenAddress,          LINELIST, NULL),
   V(SocksPolicy,                 LINELIST, NULL),
   V(SocksPolicy,                 LINELIST, NULL),
-  V(SocksPort,                   PORT,     "9050"),
+  V(SocksPort,                   LINELIST, NULL),
   V(SocksTimeout,                INTERVAL, "2 minutes"),
   V(SocksTimeout,                INTERVAL, "2 minutes"),
   OBSOLETE("StatusFetchPeriod"),
   OBSOLETE("StatusFetchPeriod"),
   V(StrictNodes,                 BOOL,     "0"),
   V(StrictNodes,                 BOOL,     "0"),
@@ -385,7 +390,7 @@ static config_var_t _option_vars[] = {
   V(TrackHostExitsExpire,        INTERVAL, "30 minutes"),
   V(TrackHostExitsExpire,        INTERVAL, "30 minutes"),
   OBSOLETE("TrafficShaping"),
   OBSOLETE("TrafficShaping"),
   V(TransListenAddress,          LINELIST, NULL),
   V(TransListenAddress,          LINELIST, NULL),
-  V(TransPort,                   PORT,     "0"),
+  V(TransPort,                   LINELIST, NULL),
   V(TunnelDirConns,              BOOL,     "1"),
   V(TunnelDirConns,              BOOL,     "1"),
   V(UpdateBridgesFromAuthority,  BOOL,     "0"),
   V(UpdateBridgesFromAuthority,  BOOL,     "0"),
   V(UseBridges,                  BOOL,     "0"),
   V(UseBridges,                  BOOL,     "0"),
@@ -577,6 +582,9 @@ static int parse_client_transport_line(const char *line, int validate_only);
 static int parse_dir_server_line(const char *line,
 static int parse_dir_server_line(const char *line,
                                  dirinfo_type_t required_type,
                                  dirinfo_type_t required_type,
                                  int validate_only);
                                  int validate_only);
+static void port_cfg_free(port_cfg_t *port);
+static int parse_client_ports(const or_options_t *options, int validate_only,
+                              char **msg_out, int *n_ports_out);
 static int validate_data_directory(or_options_t *options);
 static int validate_data_directory(or_options_t *options);
 static int write_configuration_file(const char *fname,
 static int write_configuration_file(const char *fname,
                                     const or_options_t *options);
                                     const or_options_t *options);
@@ -646,6 +654,8 @@ static or_state_t *global_state = NULL;
 static config_line_t *global_cmdline_options = NULL;
 static config_line_t *global_cmdline_options = NULL;
 /** Contents of most recently read DirPortFrontPage file. */
 /** Contents of most recently read DirPortFrontPage file. */
 static char *global_dirfrontpagecontents = NULL;
 static char *global_dirfrontpagecontents = NULL;
+/** List of port_cfg_t for client-level (SOCKS, DNS, Trans, NATD) ports. */
+static smartlist_t *configured_client_ports = NULL;
 
 
 /** Return the contents of our frontpage string, or NULL if not configured. */
 /** Return the contents of our frontpage string, or NULL if not configured. */
 const char *
 const char *
@@ -686,6 +696,9 @@ get_options(void)
 int
 int
 set_options(or_options_t *new_val, char **msg)
 set_options(or_options_t *new_val, char **msg)
 {
 {
+  int i;
+  smartlist_t *elements;
+  config_line_t *line;
   or_options_t *old_options = global_options;
   or_options_t *old_options = global_options;
   global_options = new_val;
   global_options = new_val;
   /* Note that we pass the *old* options below, for comparison. It
   /* Note that we pass the *old* options below, for comparison. It
@@ -700,7 +713,34 @@ set_options(or_options_t *new_val, char **msg)
             "Acting on config options left us in a broken state. Dying.");
             "Acting on config options left us in a broken state. Dying.");
     exit(1);
     exit(1);
   }
   }
-
+  /* Issues a CONF_CHANGED event to notify controller of the change. If Tor is
+   * just starting up then the old_options will be undefined. */
+  if (old_options) {
+    elements = smartlist_create();
+    for (i=0; options_format.vars[i].name; ++i) {
+      const config_var_t *var = &options_format.vars[i];
+      const char *var_name = var->name;
+      if (var->type == CONFIG_TYPE_LINELIST_S ||
+          var->type == CONFIG_TYPE_OBSOLETE) {
+        continue;
+      }
+      if (!option_is_same(&options_format, new_val, old_options, var_name)) {
+        line = get_assigned_option(&options_format, new_val, var_name, 1);
+
+        if (line) {
+          for (; line; line = line->next) {
+            smartlist_add(elements, line->key);
+            smartlist_add(elements, line->value);
+          }
+        } else {
+          smartlist_add(elements, (char*)options_format.vars[i].name);
+          smartlist_add(elements, NULL);
+        }
+      }
+    }
+    control_event_conf_changed(elements);
+    smartlist_free(elements);
+  }
   config_free(&options_format, old_options);
   config_free(&options_format, old_options);
 
 
   return 0;
   return 0;
@@ -758,6 +798,13 @@ config_free_all(void)
   config_free_lines(global_cmdline_options);
   config_free_lines(global_cmdline_options);
   global_cmdline_options = NULL;
   global_cmdline_options = NULL;
 
 
+  if (configured_client_ports) {
+    SMARTLIST_FOREACH(configured_client_ports,
+                      port_cfg_t *, p, tor_free(p));
+    smartlist_free(configured_client_ports);
+    configured_client_ports = NULL;
+  }
+
   tor_free(torrc_fname);
   tor_free(torrc_fname);
   tor_free(_version);
   tor_free(_version);
   tor_free(global_dirfrontpagecontents);
   tor_free(global_dirfrontpagecontents);
@@ -1004,6 +1051,7 @@ options_act_reversible(const or_options_t *old_options, char **msg)
 #endif
 #endif
 
 
   if (running_tor) {
   if (running_tor) {
+    int n_client_ports=0;
     /* We need to set the connection limit before we can open the listeners. */
     /* We need to set the connection limit before we can open the listeners. */
     if (set_max_file_descriptors((unsigned)options->ConnLimit,
     if (set_max_file_descriptors((unsigned)options->ConnLimit,
                                  &options->_ConnLimit) < 0) {
                                  &options->_ConnLimit) < 0) {
@@ -1019,6 +1067,10 @@ options_act_reversible(const or_options_t *old_options, char **msg)
       libevent_initialized = 1;
       libevent_initialized = 1;
     }
     }
 
 
+    /* Adjust the client port configuration so we can launch listeners. */
+    if (parse_client_ports(options, 0, msg, &n_client_ports))
+      return -1;
+
     /* Launch the listeners.  (We do this before we setuid, so we can bind to
     /* Launch the listeners.  (We do this before we setuid, so we can bind to
      * ports under 1024.)  We don't want to rebind if we're hibernating. */
      * ports under 1024.)  We don't want to rebind if we're hibernating. */
     if (!we_are_hibernating()) {
     if (!we_are_hibernating()) {
@@ -1068,6 +1120,9 @@ options_act_reversible(const or_options_t *old_options, char **msg)
     /* No need to roll back, since you can't change the value. */
     /* No need to roll back, since you can't change the value. */
   }
   }
 
 
+  /* Write control ports to disk as appropriate */
+  control_ports_write_to_file();
+
   if (directory_caches_v2_dir_info(options)) {
   if (directory_caches_v2_dir_info(options)) {
     size_t len = strlen(options->DataDirectory)+32;
     size_t len = strlen(options->DataDirectory)+32;
     char *fn = tor_malloc(len);
     char *fn = tor_malloc(len);
@@ -3027,6 +3082,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
   int i;
   int i;
   config_line_t *cl;
   config_line_t *cl;
   const char *uname = get_uname();
   const char *uname = get_uname();
+  int n_client_ports=0;
 #define REJECT(arg) \
 #define REJECT(arg) \
   STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
   STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
 #define COMPLAIN(arg) STMT_BEGIN log(LOG_WARN, LD_CONFIG, arg); STMT_END
 #define COMPLAIN(arg) STMT_BEGIN log(LOG_WARN, LD_CONFIG, arg); STMT_END
@@ -3050,57 +3106,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
   if (options->DirPort == 0 && options->DirListenAddress != NULL)
   if (options->DirPort == 0 && options->DirListenAddress != NULL)
     REJECT("DirPort must be defined if DirListenAddress is defined.");
     REJECT("DirPort must be defined if DirListenAddress is defined.");
 
 
-  if (options->DNSPort == 0 && options->DNSListenAddress != NULL)
-    REJECT("DNSPort must be defined if DNSListenAddress is defined.");
-
-  if (options->ControlPort == 0 && options->ControlListenAddress != NULL)
-    REJECT("ControlPort must be defined if ControlListenAddress is defined.");
-
-  if (options->TransPort == 0 && options->TransListenAddress != NULL)
-    REJECT("TransPort must be defined if TransListenAddress is defined.");
-
-  if (options->NATDPort == 0 && options->NATDListenAddress != NULL)
-    REJECT("NATDPort must be defined if NATDListenAddress is defined.");
-
-  /* Don't gripe about SocksPort 0 with SocksListenAddress set; a standard
-   * configuration does this. */
-
-  for (i = 0; i < 3; ++i) {
-    int is_socks = i==0;
-    int is_trans = i==1;
-    config_line_t *line, *opt, *old;
-    const char *tp;
-    if (is_socks) {
-      opt = options->SocksListenAddress;
-      old = old_options ? old_options->SocksListenAddress : NULL;
-      tp = "SOCKS proxy";
-    } else if (is_trans) {
-      opt = options->TransListenAddress;
-      old = old_options ? old_options->TransListenAddress : NULL;
-      tp = "transparent proxy";
-    } else {
-      opt = options->NATDListenAddress;
-      old = old_options ? old_options->NATDListenAddress : NULL;
-      tp = "natd proxy";
-    }
-
-    for (line = opt; line; line = line->next) {
-      char *address = NULL;
-      uint16_t port;
-      uint32_t addr;
-      if (parse_addr_port(LOG_WARN, line->value, &address, &addr, &port)<0)
-        continue; /* We'll warn about this later. */
-      if (!is_internal_IP(addr, 1) &&
-          (!old_options || !config_lines_eq(old, opt))) {
-        log_warn(LD_CONFIG,
-             "You specified a public address '%s' for a %s. Other "
-             "people on the Internet might find your computer and use it as "
-             "an open %s. Please don't allow this unless you have "
-             "a good reason.", address, tp, tp);
-      }
-      tor_free(address);
-    }
-  }
+  if (parse_client_ports(options, 1, msg, &n_client_ports) < 0)
+    return -1;
 
 
   if (validate_data_directory(options)<0)
   if (validate_data_directory(options)<0)
     REJECT("Invalid DataDirectory");
     REJECT("Invalid DataDirectory");
@@ -3124,8 +3131,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
         "misconfigured or something else goes wrong.");
         "misconfigured or something else goes wrong.");
 
 
   /* Special case on first boot if no Log options are given. */
   /* Special case on first boot if no Log options are given. */
-  if (!options->Logs && !options->RunAsDaemon && !from_setconf)
-    config_line_append(&options->Logs, "Log", "notice stdout");
+  if (!options->Logs && !options->RunAsDaemon && !from_setconf) {
+    if (quiet_level == 0)
+        config_line_append(&options->Logs, "Log", "notice stdout");
+    else if (quiet_level == 1)
+        config_line_append(&options->Logs, "Log", "warn stdout");
+  }
 
 
   if (options_init_logs(options, 1)<0) /* Validate the log(s) */
   if (options_init_logs(options, 1)<0) /* Validate the log(s) */
     REJECT("Failed to validate Log options. See logs for details.");
     REJECT("Failed to validate Log options. See logs for details.");
@@ -3142,9 +3153,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
     REJECT("Can't use a relative path to torrc when RunAsDaemon is set.");
     REJECT("Can't use a relative path to torrc when RunAsDaemon is set.");
 #endif
 #endif
 
 
-  if (options->SocksPort == 0 && options->TransPort == 0 &&
-      options->NATDPort == 0 && options->ORPort == 0 &&
-      options->DNSPort == 0 && !options->RendConfigLines)
+  if (n_client_ports == 0 && options->ORPort == 0 && !options->RendConfigLines)
     log(LOG_WARN, LD_CONFIG,
     log(LOG_WARN, LD_CONFIG,
         "SocksPort, TransPort, NATDPort, DNSPort, and ORPort are all "
         "SocksPort, TransPort, NATDPort, DNSPort, and ORPort are all "
         "undefined, and there aren't any hidden services configured.  "
         "undefined, and there aren't any hidden services configured.  "
@@ -3253,6 +3262,15 @@ options_validate(or_options_t *old_options, or_options_t *options,
     return -1;
     return -1;
   }
   }
 
 
+  if (options->MaxClientCircuitsPending <= 0 ||
+      options->MaxClientCircuitsPending > MAX_MAX_CLIENT_CIRCUITS_PENDING) {
+    tor_asprintf(msg,
+                 "MaxClientCircuitsPending must be between 1 and %d, but "
+                 "was set to %d", MAX_MAX_CLIENT_CIRCUITS_PENDING,
+                 options->MaxClientCircuitsPending);
+    return -1;
+  }
+
   if (validate_ports_csv(options->FirewallPorts, "FirewallPorts", msg) < 0)
   if (validate_ports_csv(options->FirewallPorts, "FirewallPorts", msg) < 0)
     return -1;
     return -1;
 
 
@@ -3892,12 +3910,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
 static int
 static int
 opt_streq(const char *s1, const char *s2)
 opt_streq(const char *s1, const char *s2)
 {
 {
-  if (!s1 && !s2)
-    return 1;
-  else if (s1 && s2 && !strcmp(s1,s2))
-    return 1;
-  else
-    return 0;
+  return 0 == strcmp_opt(s1, s2);
 }
 }
 
 
 /** Check if any of the previous options have changed but aren't allowed to. */
 /** Check if any of the previous options have changed but aren't allowed to. */
@@ -4885,6 +4898,365 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type,
   return r;
   return r;
 }
 }
 
 
+/** Free all storage held in <b>port</b> */
+static void
+port_cfg_free(port_cfg_t *port)
+{
+  tor_free(port);
+}
+
+/** Warn for every port in <b>ports</b> that is not on a loopback address. */
+static void
+warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname)
+{
+  SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) {
+    if (!tor_addr_is_loopback(&port->addr)) {
+      log_warn(LD_CONFIG, "You specified a public address for %sPort. "
+               "Other people on the Internet might find your computer and "
+               "use it as an open proxy. Please don't allow this unless you "
+               "have a good reason.", portname);
+    }
+  } SMARTLIST_FOREACH_END(port);
+}
+
+#define CL_PORT_NO_OPTIONS    (1u<<0)
+#define CL_PORT_WARN_NONLOCAL (1u<<1)
+#define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2)
+
+/**
+ * Parse port configuration for a single client port type.
+ *
+ * Read entries of the "FooPort" type from the list <b>ports</b>, and
+ * entries of the "FooListenAddress" type from the list
+ * <b>listenaddrs</b>.  Two syntaxes are supported: a legacy syntax
+ * where FooPort is at most a single entry containing a port number and
+ * where FooListenAddress has any number of address:port combinations;
+ * and a new syntax where there are no FooListenAddress entries and
+ * where FooPort can have any number of entries of the format
+ * "[Address:][Port] IsolationOptions".
+ *
+ * In log messages, describe the port type as <b>portname</b>.
+ *
+ * If no address is specified, default to <b>defaultaddr</b>.  If no
+ * FooPort is given, default to defaultport (if 0, there is no default).
+ *
+ * If CL_PORT_NO_OPTIONS is set in <b>flags</b>, do not allow stream
+ * isolation options in the FooPort entries.
+ *
+ * If CL_PORT_WARN_NONLOCAL is set in <b>flags</b>, warn if any of the
+ * ports are not on a local address.
+ *
+ * Unless CL_PORT_ALLOW_EXTRA_LISTENADDR is set in <b>flags</b>, warn
+ * if FooListenAddress is set but FooPort is 0.
+ *
+ * On success, if <b>out</b> is given, add a new port_cfg_t entry to
+ * <b>out</b> for every port that the client should listen on.  Return 0
+ * on success, -1 on failure.
+ */
+static int
+parse_client_port_config(smartlist_t *out,
+                         const config_line_t *ports,
+                         const config_line_t *listenaddrs,
+                         const char *portname,
+                         int listener_type,
+                         const char *defaultaddr,
+                         int defaultport,
+                         unsigned flags)
+{
+  smartlist_t *elts;
+  int retval = -1;
+  const unsigned allow_client_options = !(flags & CL_PORT_NO_OPTIONS);
+  const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL;
+  const unsigned allow_spurious_listenaddr =
+    flags & CL_PORT_ALLOW_EXTRA_LISTENADDR;
+
+  /* FooListenAddress is deprecated; let's make it work like it used to work,
+   * though. */
+  if (listenaddrs) {
+    int mainport = defaultport;
+
+   if (ports && ports->next) {
+      log_warn(LD_CONFIG, "%sListenAddress can't be used when there are "
+               "multiple %sPort lines", portname, portname);
+      return -1;
+   } else if (ports) {
+     if (!strcmp(ports->value, "auto")) {
+       mainport = CFG_AUTO_PORT;
+     } else {
+       int ok;
+       mainport = (int)tor_parse_long(ports->value, 10, 0, 65535, &ok, NULL);
+       if (!ok) {
+         log_warn(LD_CONFIG, "%sListenAddress can only be used with a single "
+                 "%sPort with value \"auto\" or 1-65535.", portname, portname);
+         return -1;
+       }
+     }
+   }
+
+   if (mainport == 0) {
+     if (allow_spurious_listenaddr)
+       return 1;
+     log_warn(LD_CONFIG, "%sPort must be defined if %sListenAddress is used",
+              portname, portname);
+     return -1;
+   }
+
+   for (; listenaddrs; listenaddrs = listenaddrs->next) {
+     tor_addr_t addr;
+     uint16_t port = 0;
+     if (tor_addr_port_parse(listenaddrs->value, &addr, &port) < 0) {
+       log_warn(LD_CONFIG, "Unable to parse %sListenAddress '%s'",
+                portname, listenaddrs->value);
+       return -1;
+     }
+     if (out) {
+       port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
+       cfg->type = listener_type;
+       cfg->port = port ? port : defaultport;
+       tor_addr_copy(&cfg->addr, &addr);
+       cfg->session_group = SESSION_GROUP_UNSET;
+       cfg->isolation_flags = ISO_DEFAULT;
+       smartlist_add(out, cfg);
+     }
+   }
+
+   if (warn_nonlocal && out)
+     warn_nonlocal_client_ports(out, portname);
+   return 0;
+  } /* end if (listenaddrs) */
+
+  /* No ListenAddress lines. If there's no FooPort, then maybe make a default
+   * one. */
+  if (! ports) {
+    if (defaultport && out) {
+       port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
+       cfg->type = listener_type;
+       cfg->port = defaultport;
+       tor_addr_from_str(&cfg->addr, defaultaddr);
+       cfg->session_group = SESSION_GROUP_UNSET;
+       cfg->isolation_flags = ISO_DEFAULT;
+       smartlist_add(out, cfg);
+    }
+    return 0;
+  }
+
+  /* At last we can actually parse the FooPort lines.  The syntax is:
+   * [Addr:](Port|auto) [Options].*/
+  elts = smartlist_create();
+
+  for (; ports; ports = ports->next) {
+    tor_addr_t addr;
+    int port;
+    int sessiongroup = SESSION_GROUP_UNSET;
+    unsigned isolation = ISO_DEFAULT;
+
+    char *addrport;
+    uint16_t ptmp=0;
+    int ok;
+
+    smartlist_split_string(elts, ports->value, NULL,
+                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+    if (smartlist_len(elts) == 0) {
+      log_warn(LD_CONFIG, "Invalid %sPort line with no value", portname);
+      goto err;
+    }
+
+    if (!allow_client_options && smartlist_len(elts) > 1) {
+      log_warn(LD_CONFIG, "Too many options on %sPort line", portname);
+      goto err;
+    }
+
+    /* Now parse the addr/port value */
+    addrport = smartlist_get(elts, 0);
+    if (!strcmp(addrport, "auto")) {
+      port = CFG_AUTO_PORT;
+      tor_addr_from_str(&addr, defaultaddr);
+    } else if (!strcasecmpend(addrport, ":auto")) {
+      char *addrtmp = tor_strndup(addrport, strlen(addrport)-5);
+      port = CFG_AUTO_PORT;
+      if (tor_addr_port_parse(addrtmp, &addr, &ptmp)<0 || ptmp) {
+        log_warn(LD_CONFIG, "Invalid address '%s' for %sPort",
+                 escaped(addrport), portname);
+        tor_free(addrtmp);
+        goto err;
+      }
+    } else {
+      /* Try parsing integer port before address, because, who knows?
+         "9050" might be a valid address. */
+      port = (int) tor_parse_long(addrport, 10, 0, 65535, &ok, NULL);
+      if (ok) {
+        tor_addr_from_str(&addr, defaultaddr);
+      } else if (tor_addr_port_parse(addrport, &addr, &ptmp) == 0) {
+        if (ptmp == 0) {
+          log_warn(LD_CONFIG, "%sPort line has address but no port", portname);
+          goto err;
+        }
+        port = ptmp;
+      } else {
+        log_warn(LD_CONFIG, "Couldn't parse address '%s' for %sPort",
+                 escaped(addrport), portname);
+        goto err;
+      }
+    }
+
+    /* Now parse the rest of the options, if any. */
+    SMARTLIST_FOREACH_BEGIN(elts, char *, elt) {
+      int no = 0, isoflag = 0;
+      const char *elt_orig = elt;
+      if (elt_sl_idx == 0)
+        continue; /* Skip addr:port */
+      if (!strcasecmpstart(elt, "SessionGroup=")) {
+        int group = (int)tor_parse_long(elt+strlen("SessionGroup="),
+                                        10, 0, INT_MAX, &ok, NULL);
+        if (!ok) {
+          log_warn(LD_CONFIG, "Invalid %sPort option '%s'",
+                   portname, escaped(elt));
+          goto err;
+        }
+        if (sessiongroup >= 0) {
+          log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort",
+                   portname);
+          goto err;
+        }
+        sessiongroup = group;
+        continue;
+      }
+
+      if (!strcasecmpstart(elt, "No")) {
+        no = 1;
+        elt += 2;
+      }
+      if (!strcasecmpend(elt, "s"))
+        elt[strlen(elt)-1] = '\0'; /* kill plurals. */
+
+      if (!strcasecmp(elt, "IsolateDestPort")) {
+        isoflag = ISO_DESTPORT;
+      } else if (!strcasecmp(elt, "IsolateDestAddr")) {
+        isoflag = ISO_DESTADDR;
+      } else if (!strcasecmp(elt, "IsolateSOCKSAuth")) {
+        isoflag = ISO_SOCKSAUTH;
+      } else if (!strcasecmp(elt, "IsolateClientProtocol")) {
+        isoflag = ISO_CLIENTPROTO;
+      } else if (!strcasecmp(elt, "IsolateClientAddr")) {
+        isoflag = ISO_CLIENTADDR;
+      } else {
+        log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'",
+                 portname, escaped(elt_orig));
+      }
+
+      if (no) {
+        isolation &= ~isoflag;
+      } else {
+        isolation |= isoflag;
+      }
+    } SMARTLIST_FOREACH_END(elt);
+
+    if (out && port) {
+      port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
+      cfg->type = listener_type;
+      cfg->port = port;
+      tor_addr_copy(&cfg->addr, &addr);
+      cfg->session_group = sessiongroup;
+      cfg->isolation_flags = isolation;
+      smartlist_add(out, cfg);
+    }
+    SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
+    smartlist_clear(elts);
+  }
+
+  if (warn_nonlocal && out)
+    warn_nonlocal_client_ports(out, portname);
+
+  retval = 0;
+ err:
+  SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
+  smartlist_free(elts);
+  return retval;
+}
+
+/** Parse all client port types (Socks, DNS, Trans, NATD) from
+ * <b>options</b>. On success, set *<b>n_ports_out</b> to the number of
+ * ports that are listed and return 0.  On failure, set *<b>msg</b> to a
+ * description of the problem and return -1.
+ *
+ * If <b>validate_only</b> is false, set configured_client_ports to the
+ * new list of ports parsed from <b>options</b>.
+ **/
+static int
+parse_client_ports(const or_options_t *options, int validate_only,
+                   char **msg, int *n_ports_out)
+{
+  smartlist_t *ports;
+  int retval = -1;
+
+  ports = smartlist_create();
+
+  *n_ports_out = 0;
+
+  if (parse_client_port_config(ports,
+             options->SocksPort, options->SocksListenAddress,
+             "Socks", CONN_TYPE_AP_LISTENER,
+             "127.0.0.1", 9050,
+             CL_PORT_WARN_NONLOCAL|CL_PORT_ALLOW_EXTRA_LISTENADDR) < 0) {
+    *msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration");
+    goto err;
+  }
+  if (parse_client_port_config(ports,
+                               options->DNSPort, options->DNSListenAddress,
+                               "DNS", CONN_TYPE_AP_DNS_LISTENER,
+                               "127.0.0.1", 0,
+                               CL_PORT_WARN_NONLOCAL) < 0) {
+    *msg = tor_strdup("Invalid DNSPort/DNSListenAddress configuration");
+    goto err;
+  }
+  if (parse_client_port_config(ports,
+                               options->TransPort, options->TransListenAddress,
+                               "Trans", CONN_TYPE_AP_TRANS_LISTENER,
+                               "127.0.0.1", 0,
+                               CL_PORT_WARN_NONLOCAL) < 0) {
+    *msg = tor_strdup("Invalid TransPort/TransListenAddress configuration");
+    goto err;
+  }
+  if (parse_client_port_config(ports,
+                               options->NATDPort, options->NATDListenAddress,
+                               "NATD", CONN_TYPE_AP_NATD_LISTENER,
+                               "127.0.0.1", 0,
+                               CL_PORT_WARN_NONLOCAL) < 0) {
+    *msg = tor_strdup("Invalid NatdPort/NatdListenAddress configuration");
+    goto err;
+  }
+
+  *n_ports_out = smartlist_len(ports);
+
+  if (!validate_only) {
+    if (configured_client_ports) {
+      SMARTLIST_FOREACH(configured_client_ports,
+                        port_cfg_t *, p, port_cfg_free(p));
+      smartlist_free(configured_client_ports);
+    }
+    configured_client_ports = ports;
+    ports = NULL; /* prevent free below. */
+  }
+
+  retval = 0;
+ err:
+  if (ports) {
+    SMARTLIST_FOREACH(ports, port_cfg_t *, p, port_cfg_free(p));
+    smartlist_free(ports);
+  }
+  return retval;
+}
+
+/** Return a list of port_cfg_t for client ports parsed from the
+ * options. */
+const smartlist_t *
+get_configured_client_ports(void)
+{
+  if (!configured_client_ports)
+    configured_client_ports = smartlist_create();
+  return configured_client_ports;
+}
+
 /** Adjust the value of options->DataDirectory, or fill it in if it's
 /** Adjust the value of options->DataDirectory, or fill it in if it's
  * absent. Return 0 on success, -1 on failure. */
  * absent. Return 0 on success, -1 on failure. */
 static int
 static int

+ 2 - 0
src/or/config.h

@@ -64,6 +64,8 @@ or_state_t *get_or_state(void);
 int did_last_state_file_write_fail(void);
 int did_last_state_file_write_fail(void);
 int or_state_save(time_t now);
 int or_state_save(time_t now);
 
 
+const smartlist_t *get_configured_client_ports(void);
+
 int options_need_geoip_info(const or_options_t *options,
 int options_need_geoip_info(const or_options_t *options,
                             const char **reason_out);
                             const char **reason_out);
 int getinfo_helper_config(control_connection_t *conn,
 int getinfo_helper_config(control_connection_t *conn,

+ 315 - 285
src/or/connection.c

@@ -43,11 +43,12 @@
 static connection_t *connection_create_listener(
 static connection_t *connection_create_listener(
                                const struct sockaddr *listensockaddr,
                                const struct sockaddr *listensockaddr,
                                socklen_t listensocklen, int type,
                                socklen_t listensocklen, int type,
-                               char* address);
+                               const char *address,
+                               const port_cfg_t *portcfg);
 static void connection_init(time_t now, connection_t *conn, int type,
 static void connection_init(time_t now, connection_t *conn, int type,
                             int socket_family);
                             int socket_family);
 static int connection_init_accepted_conn(connection_t *conn,
 static int connection_init_accepted_conn(connection_t *conn,
-                                         uint8_t listener_type);
+                          const listener_connection_t *listener);
 static int connection_handle_listener_read(connection_t *conn, int new_type);
 static int connection_handle_listener_read(connection_t *conn, int new_type);
 #ifndef USE_BUFFEREVENTS
 #ifndef USE_BUFFEREVENTS
 static int connection_bucket_should_increase(int bucket,
 static int connection_bucket_should_increase(int bucket,
@@ -76,6 +77,15 @@ static uint32_t last_interface_ip = 0;
  * Used to detect IP address changes. */
  * Used to detect IP address changes. */
 static smartlist_t *outgoing_addrs = NULL;
 static smartlist_t *outgoing_addrs = NULL;
 
 
+#define CASE_ANY_LISTENER_TYPE \
+    case CONN_TYPE_OR_LISTENER: \
+    case CONN_TYPE_AP_LISTENER: \
+    case CONN_TYPE_DIR_LISTENER: \
+    case CONN_TYPE_CONTROL_LISTENER: \
+    case CONN_TYPE_AP_TRANS_LISTENER: \
+    case CONN_TYPE_AP_NATD_LISTENER: \
+    case CONN_TYPE_AP_DNS_LISTENER
+
 /**************************************************************/
 /**************************************************************/
 
 
 /**
 /**
@@ -116,13 +126,7 @@ conn_state_to_string(int type, int state)
 {
 {
   static char buf[96];
   static char buf[96];
   switch (type) {
   switch (type) {
-    case CONN_TYPE_OR_LISTENER:
-    case CONN_TYPE_AP_LISTENER:
-    case CONN_TYPE_AP_TRANS_LISTENER:
-    case CONN_TYPE_AP_NATD_LISTENER:
-    case CONN_TYPE_AP_DNS_LISTENER:
-    case CONN_TYPE_DIR_LISTENER:
-    case CONN_TYPE_CONTROL_LISTENER:
+    CASE_ANY_LISTENER_TYPE:
       if (state == LISTENER_STATE_READY)
       if (state == LISTENER_STATE_READY)
         return "ready";
         return "ready";
       break;
       break;
@@ -265,6 +269,17 @@ control_connection_new(int socket_family)
   return control_conn;
   return control_conn;
 }
 }
 
 
+/** Allocate and return a new listener_connection_t, initialized as by
+ * connection_init(). */
+listener_connection_t *
+listener_connection_new(int type, int socket_family)
+{
+  listener_connection_t *listener_conn =
+    tor_malloc_zero(sizeof(listener_connection_t));
+  connection_init(time(NULL), TO_CONN(listener_conn), type, socket_family);
+  return listener_conn;
+}
+
 /** Allocate, initialize, and return a new connection_t subtype of <b>type</b>
 /** Allocate, initialize, and return a new connection_t subtype of <b>type</b>
  * to make or receive connections of address family <b>socket_family</b>.  The
  * to make or receive connections of address family <b>socket_family</b>.  The
  * type should be one of the CONN_TYPE_* constants. */
  * type should be one of the CONN_TYPE_* constants. */
@@ -285,6 +300,9 @@ connection_new(int type, int socket_family)
     case CONN_TYPE_CONTROL:
     case CONN_TYPE_CONTROL:
       return TO_CONN(control_connection_new(socket_family));
       return TO_CONN(control_connection_new(socket_family));
 
 
+    CASE_ANY_LISTENER_TYPE:
+      return TO_CONN(listener_connection_new(type, socket_family));
+
     default: {
     default: {
       connection_t *conn = tor_malloc_zero(sizeof(connection_t));
       connection_t *conn = tor_malloc_zero(sizeof(connection_t));
       connection_init(time(NULL), conn, type, socket_family);
       connection_init(time(NULL), conn, type, socket_family);
@@ -325,6 +343,9 @@ connection_init(time_t now, connection_t *conn, int type, int socket_family)
     case CONN_TYPE_CONTROL:
     case CONN_TYPE_CONTROL:
       conn->magic = CONTROL_CONNECTION_MAGIC;
       conn->magic = CONTROL_CONNECTION_MAGIC;
       break;
       break;
+    CASE_ANY_LISTENER_TYPE:
+      conn->magic = LISTENER_CONNECTION_MAGIC;
+      break;
     default:
     default:
       conn->magic = BASE_CONNECTION_MAGIC;
       conn->magic = BASE_CONNECTION_MAGIC;
       break;
       break;
@@ -396,6 +417,11 @@ _connection_free(connection_t *conn)
       mem = TO_CONTROL_CONN(conn);
       mem = TO_CONTROL_CONN(conn);
       memlen = sizeof(control_connection_t);
       memlen = sizeof(control_connection_t);
       break;
       break;
+    CASE_ANY_LISTENER_TYPE:
+      tor_assert(conn->magic == LISTENER_CONNECTION_MAGIC);
+      mem = TO_LISTENER_CONN(conn);
+      memlen = sizeof(listener_connection_t);
+      break;
     default:
     default:
       tor_assert(conn->magic == BASE_CONNECTION_MAGIC);
       tor_assert(conn->magic == BASE_CONNECTION_MAGIC);
       mem = conn;
       mem = conn;
@@ -442,9 +468,15 @@ _connection_free(connection_t *conn)
   if (CONN_IS_EDGE(conn)) {
   if (CONN_IS_EDGE(conn)) {
     edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
     edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
     tor_free(edge_conn->chosen_exit_name);
     tor_free(edge_conn->chosen_exit_name);
+    tor_free(edge_conn->original_dest_address);
     if (edge_conn->socks_request)
     if (edge_conn->socks_request)
       socks_request_free(edge_conn->socks_request);
       socks_request_free(edge_conn->socks_request);
-
+    if (edge_conn->pending_optimistic_data) {
+      generic_buffer_free(edge_conn->pending_optimistic_data);
+    }
+    if (edge_conn->sending_optimistic_data) {
+      generic_buffer_free(edge_conn->sending_optimistic_data);
+    }
     rend_data_free(edge_conn->rend_data);
     rend_data_free(edge_conn->rend_data);
   }
   }
   if (conn->type == CONN_TYPE_CONTROL) {
   if (conn->type == CONN_TYPE_CONTROL) {
@@ -529,41 +561,6 @@ connection_free(connection_t *conn)
   _connection_free(conn);
   _connection_free(conn);
 }
 }
 
 
-/** Call _connection_free() on every connection in our array, and release all
- * storage held by connection.c. This is used by cpuworkers and dnsworkers
- * when they fork, so they don't keep resources held open (especially
- * sockets).
- *
- * Don't do the checks in connection_free(), because they will
- * fail.
- */
-void
-connection_free_all(void)
-{
-  smartlist_t *conns = get_connection_array();
-
-  /* We don't want to log any messages to controllers. */
-  SMARTLIST_FOREACH(conns, connection_t *, conn,
-    if (conn->type == CONN_TYPE_CONTROL)
-      TO_CONTROL_CONN(conn)->event_mask = 0);
-
-  control_update_global_event_mask();
-
-  /* Unlink everything from the identity map. */
-  connection_or_clear_identity_map();
-
-  /* Clear out our list of broken connections */
-  clear_broken_connection_map(0);
-
-  SMARTLIST_FOREACH(conns, connection_t *, conn, _connection_free(conn));
-
-  if (outgoing_addrs) {
-    SMARTLIST_FOREACH(outgoing_addrs, void*, addr, tor_free(addr));
-    smartlist_free(outgoing_addrs);
-    outgoing_addrs = NULL;
-  }
-}
-
 /**
 /**
  * Called when we're about to finally unlink and free a connection:
  * Called when we're about to finally unlink and free a connection:
  * perform necessary accounting and cleanup
  * perform necessary accounting and cleanup
@@ -701,48 +698,6 @@ connection_expire_held_open(void)
   });
   });
 }
 }
 
 
-/** Create an AF_INET listenaddr struct.
- * <b>listenaddress</b> provides the host and optionally the port information
- * for the new structure.  If no port is provided in <b>listenaddress</b> then
- * <b>listenport</b> is used.
- *
- * If not NULL <b>readable_address</b> will contain a copy of the host part of
- * <b>listenaddress</b>.
- *
- * The listenaddr struct has to be freed by the caller.
- */
-static struct sockaddr_in *
-create_inet_sockaddr(const char *listenaddress, int listenport,
-                     char **readable_address, socklen_t *socklen_out) {
-  struct sockaddr_in *listenaddr = NULL;
-  uint32_t addr;
-  uint16_t usePort = 0;
-
-  if (parse_addr_port(LOG_WARN,
-                      listenaddress, readable_address, &addr, &usePort)<0) {
-    log_warn(LD_CONFIG,
-             "Error parsing/resolving ListenAddress %s", listenaddress);
-    goto err;
-  }
-  if (usePort==0) {
-    if (listenport != CFG_AUTO_PORT)
-      usePort = listenport;
-  }
-
-  listenaddr = tor_malloc_zero(sizeof(struct sockaddr_in));
-  listenaddr->sin_addr.s_addr = htonl(addr);
-  listenaddr->sin_family = AF_INET;
-  listenaddr->sin_port = htons((uint16_t) usePort);
-
-  *socklen_out = sizeof(struct sockaddr_in);
-
-  return listenaddr;
-
- err:
-  tor_free(listenaddr);
-  return NULL;
-}
-
 #ifdef HAVE_SYS_UN_H
 #ifdef HAVE_SYS_UN_H
 /** Create an AF_UNIX listenaddr struct.
 /** Create an AF_UNIX listenaddr struct.
  * <b>listenaddress</b> provides the path to the Unix socket.
  * <b>listenaddress</b> provides the path to the Unix socket.
@@ -877,12 +832,16 @@ make_socket_reuseable(tor_socket_t sock)
 static connection_t *
 static connection_t *
 connection_create_listener(const struct sockaddr *listensockaddr,
 connection_create_listener(const struct sockaddr *listensockaddr,
                            socklen_t socklen,
                            socklen_t socklen,
-                           int type, char* address)
+                           int type, const char *address,
+                           const port_cfg_t *port_cfg)
 {
 {
+  listener_connection_t *lis_conn;
   connection_t *conn;
   connection_t *conn;
   tor_socket_t s; /* the socket we're going to make */
   tor_socket_t s; /* the socket we're going to make */
   uint16_t usePort = 0, gotPort = 0;
   uint16_t usePort = 0, gotPort = 0;
   int start_reading = 0;
   int start_reading = 0;
+  static int global_next_session_group = SESSION_GROUP_FIRST_AUTO;
+  tor_addr_t addr;
 
 
   if (get_n_open_sockets() >= get_options()->_ConnLimit-1) {
   if (get_n_open_sockets() >= get_options()->_ConnLimit-1) {
     warn_too_many_conns();
     warn_too_many_conns();
@@ -890,7 +849,6 @@ connection_create_listener(const struct sockaddr *listensockaddr,
   }
   }
 
 
   if (listensockaddr->sa_family == AF_INET) {
   if (listensockaddr->sa_family == AF_INET) {
-    tor_addr_t addr;
     int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
     int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
     if (is_tcp)
     if (is_tcp)
       start_reading = 1;
       start_reading = 1;
@@ -958,6 +916,8 @@ connection_create_listener(const struct sockaddr *listensockaddr,
     log_notice(LD_NET, "Opening %s on %s",
     log_notice(LD_NET, "Opening %s on %s",
                conn_type_to_string(type), address);
                conn_type_to_string(type), address);
 
 
+    tor_addr_make_unspec(&addr);
+
     if (unlink(address) < 0 && errno != ENOENT) {
     if (unlink(address) < 0 && errno != ENOENT) {
       log_warn(LD_NET, "Could not unlink %s: %s", address,
       log_warn(LD_NET, "Could not unlink %s: %s", address,
                        strerror(errno));
                        strerror(errno));
@@ -999,11 +959,23 @@ connection_create_listener(const struct sockaddr *listensockaddr,
 
 
   set_socket_nonblocking(s);
   set_socket_nonblocking(s);
 
 
-  conn = connection_new(type, listensockaddr->sa_family);
+  lis_conn = listener_connection_new(type, listensockaddr->sa_family);
+  conn = TO_CONN(lis_conn);
   conn->socket_family = listensockaddr->sa_family;
   conn->socket_family = listensockaddr->sa_family;
   conn->s = s;
   conn->s = s;
   conn->address = tor_strdup(address);
   conn->address = tor_strdup(address);
   conn->port = gotPort;
   conn->port = gotPort;
+  tor_addr_copy(&conn->addr, &addr);
+
+  if (port_cfg->isolation_flags) {
+    lis_conn->isolation_flags = port_cfg->isolation_flags;
+    if (port_cfg->session_group >= 0) {
+      lis_conn->session_group = port_cfg->session_group;
+    } else {
+      /* XXXX023 This can wrap after ~INT_MAX ports are opened. */
+      lis_conn->session_group = global_next_session_group--;
+    }
+  }
 
 
   if (connection_add(conn) < 0) { /* no space, forget it */
   if (connection_add(conn) < 0) { /* no space, forget it */
     log_warn(LD_NET,"connection_add for listener failed. Giving up.");
     log_warn(LD_NET,"connection_add for listener failed. Giving up.");
@@ -1015,9 +987,6 @@ connection_create_listener(const struct sockaddr *listensockaddr,
          "%s listening on port %u.",
          "%s listening on port %u.",
          conn_type_to_string(type), gotPort);
          conn_type_to_string(type), gotPort);
 
 
-  if (type == CONN_TYPE_CONTROL_LISTENER)
-    control_ports_write_to_file();
-
   conn->state = LISTENER_STATE_READY;
   conn->state = LISTENER_STATE_READY;
   if (start_reading) {
   if (start_reading) {
     connection_start_reading(conn);
     connection_start_reading(conn);
@@ -1217,7 +1186,7 @@ connection_handle_listener_read(connection_t *conn, int new_type)
     return 0; /* no need to tear down the parent */
     return 0; /* no need to tear down the parent */
   }
   }
 
 
-  if (connection_init_accepted_conn(newconn, conn->type) < 0) {
+  if (connection_init_accepted_conn(newconn, TO_LISTENER_CONN(conn)) < 0) {
     if (! newconn->marked_for_close)
     if (! newconn->marked_for_close)
       connection_mark_for_close(newconn);
       connection_mark_for_close(newconn);
     return 0;
     return 0;
@@ -1231,7 +1200,8 @@ connection_handle_listener_read(connection_t *conn, int new_type)
  * and place it in circuit_wait.
  * and place it in circuit_wait.
  */
  */
 static int
 static int
-connection_init_accepted_conn(connection_t *conn, uint8_t listener_type)
+connection_init_accepted_conn(connection_t *conn,
+                              const listener_connection_t *listener)
 {
 {
   connection_start_reading(conn);
   connection_start_reading(conn);
 
 
@@ -1240,7 +1210,11 @@ connection_init_accepted_conn(connection_t *conn, uint8_t listener_type)
       control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0);
       control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0);
       return connection_tls_start_handshake(TO_OR_CONN(conn), 1);
       return connection_tls_start_handshake(TO_OR_CONN(conn), 1);
     case CONN_TYPE_AP:
     case CONN_TYPE_AP:
-      switch (listener_type) {
+      TO_EDGE_CONN(conn)->isolation_flags = listener->isolation_flags;
+      TO_EDGE_CONN(conn)->session_group = listener->session_group;
+      TO_EDGE_CONN(conn)->nym_epoch = get_signewnym_epoch();
+      TO_EDGE_CONN(conn)->socks_request->listener_type = listener->_base.type;
+      switch (TO_CONN(listener)->type) {
         case CONN_TYPE_AP_LISTENER:
         case CONN_TYPE_AP_LISTENER:
           conn->state = AP_CONN_STATE_SOCKS_WAIT;
           conn->state = AP_CONN_STATE_SOCKS_WAIT;
           break;
           break;
@@ -1741,6 +1715,113 @@ connection_read_proxy_handshake(connection_t *conn)
   return ret;
   return ret;
 }
 }
 
 
+/** Given a list of listener connections in <b>old_conns</b>, and list of
+ * port_cfg_t entries in <b>ports</b>, open a new listener for every port in
+ * <b>ports</b> that does not already have a listener in <b>old_conns</b>.
+ *
+ * Remove from <b>old_conns</b> every connection that has a corresponding
+ * entry in <b>ports</b>.  Add to <b>new_conns</b> new every connection we
+ * launch.
+ *
+ * Return 0 on success, -1 on failure.
+ **/
+static int
+retry_listener_ports(smartlist_t *old_conns,
+                     const smartlist_t *ports,
+                     smartlist_t *new_conns)
+{
+  smartlist_t *launch = smartlist_create();
+  int r = 0;
+
+  smartlist_add_all(launch, ports);
+
+  /* Iterate through old_conns, comparing it to launch: remove from both lists
+   * each pair of elements that corresponds to the same port. */
+  SMARTLIST_FOREACH_BEGIN(old_conns, connection_t *, conn) {
+    const port_cfg_t *found_port = NULL;
+
+    /* Okay, so this is a listener.  Is it configured? */
+    SMARTLIST_FOREACH_BEGIN(launch, const port_cfg_t *, wanted) {
+      if (conn->type != wanted->type)
+        continue;
+      if ((conn->socket_family != AF_UNIX && wanted->is_unix_addr) ||
+          (conn->socket_family == AF_UNIX && ! wanted->is_unix_addr))
+        continue;
+
+      if (wanted->is_unix_addr) {
+        if (conn->socket_family == AF_UNIX &&
+            !strcmp(wanted->unix_addr, conn->address)) {
+          found_port = wanted;
+          break;
+        }
+      } else {
+        int port_matches;
+        if (wanted->port == CFG_AUTO_PORT) {
+          port_matches = 1;
+        } else {
+          port_matches = (wanted->port == conn->port);
+        }
+        if (port_matches && tor_addr_eq(&wanted->addr, &conn->addr)) {
+          found_port = wanted;
+          break;
+        }
+      }
+    } SMARTLIST_FOREACH_END(wanted);
+
+    if (found_port) {
+      /* This listener is already running; we don't need to launch it. */
+      //log_debug(LD_NET, "Already have %s on %s:%d",
+      //    conn_type_to_string(found_port->type), conn->address, conn->port);
+      smartlist_remove(launch, found_port);
+      /* And we can remove the connection from old_conns too. */
+      SMARTLIST_DEL_CURRENT(old_conns, conn);
+    }
+  } SMARTLIST_FOREACH_END(conn);
+
+  /* Now open all the listeners that are configured but not opened. */
+  SMARTLIST_FOREACH_BEGIN(launch, const port_cfg_t *, port) {
+    struct sockaddr *listensockaddr;
+    socklen_t listensocklen = 0;
+    char *address=NULL;
+    connection_t *conn;
+    int real_port = port->port == CFG_AUTO_PORT ? 0 : port->port;
+    tor_assert(real_port <= UINT16_MAX);
+
+    if (port->is_unix_addr) {
+      listensockaddr = (struct sockaddr *)
+        create_unix_sockaddr(port->unix_addr,
+                             &address, &listensocklen);
+    } else {
+      listensockaddr = tor_malloc(sizeof(struct sockaddr_storage));
+      listensocklen = tor_addr_to_sockaddr(&port->addr,
+                                           real_port,
+                                           listensockaddr,
+                                           sizeof(struct sockaddr_storage));
+      address = tor_dup_addr(&port->addr);
+    }
+
+    if (listensockaddr) {
+      conn = connection_create_listener(listensockaddr, listensocklen,
+                                        port->type, address, port);
+      tor_free(listensockaddr);
+      tor_free(address);
+    } else {
+      conn = NULL;
+    }
+
+    if (!conn) {
+      r = -1;
+    } else {
+      if (new_conns)
+        smartlist_add(new_conns, conn);
+    }
+  } SMARTLIST_FOREACH_END(port);
+
+  smartlist_free(launch);
+
+  return r;
+}
+
 /**
 /**
  * Launch any configured listener connections of type <b>type</b>.  (A
  * Launch any configured listener connections of type <b>type</b>.  (A
  * listener is configured if <b>port_option</b> is non-zero.  If any
  * listener is configured if <b>port_option</b> is non-zero.  If any
@@ -1748,168 +1829,73 @@ connection_read_proxy_handshake(connection_t *conn)
  * connection binding to each one.  Otherwise, create a single
  * connection binding to each one.  Otherwise, create a single
  * connection binding to the address <b>default_addr</b>.)
  * connection binding to the address <b>default_addr</b>.)
  *
  *
- * Only launch the listeners of this type that are not already open, and
- * only close listeners that are no longer wanted.  Existing listeners
- * that are still configured are not touched.
+ * We assume that we're starting with a list of existing listener connection_t
+ * pointers in <b>old_conns</b>: we do not launch listeners that are already
+ * in that list.  Instead, we just remove them from the list.
  *
  *
- * If <b>disable_all_conns</b> is set, then never open new conns, and
- * close the existing ones.
- *
- * Add all old conns that should be closed to <b>replaced_conns</b>.
- * Add all new connections to <b>new_conns</b>.
+ * All new connections we launch are added to <b>new_conns</b>.
  */
  */
 static int
 static int
-retry_listeners(int type, config_line_t *cfg,
+retry_listeners(smartlist_t *old_conns,
+                int type, const config_line_t *cfg,
                 int port_option, const char *default_addr,
                 int port_option, const char *default_addr,
-                smartlist_t *replaced_conns,
                 smartlist_t *new_conns,
                 smartlist_t *new_conns,
-                int disable_all_conns,
-                int socket_family)
+                int is_sockaddr_un)
 {
 {
-  smartlist_t *launch = smartlist_create(), *conns;
-  int free_launch_elts = 1;
-  int r;
-  config_line_t *c;
-  connection_t *conn;
-  config_line_t *line;
-
-  tor_assert(socket_family == AF_INET || socket_family == AF_UNIX);
+  smartlist_t *ports = smartlist_create();
+  tor_addr_t dflt_addr;
+  int retval = 0;
 
 
-  if (cfg && port_option) {
-    for (c = cfg; c; c = c->next) {
-      smartlist_add(launch, c);
-    }
-    free_launch_elts = 0;
-  } else if (port_option) {
-    line = tor_malloc_zero(sizeof(config_line_t));
-    line->key = tor_strdup("");
-    line->value = tor_strdup(default_addr);
-    smartlist_add(launch, line);
+  if (default_addr) {
+    tor_addr_from_str(&dflt_addr, default_addr);
+  } else {
+    tor_addr_make_unspec(&dflt_addr);
   }
   }
 
 
-  /*
-  SMARTLIST_FOREACH(launch, config_line_t *, l,
-                    log_fn(LOG_NOTICE, "#%s#%s", l->key, l->value));
-  */
-
-  conns = get_connection_array();
-  SMARTLIST_FOREACH(conns, connection_t *, conn,
-  {
-    if (conn->type != type ||
-        conn->socket_family != socket_family ||
-        conn->marked_for_close)
-      continue;
-    /* Okay, so this is a listener.  Is it configured? */
-    line = NULL;
-    SMARTLIST_FOREACH(launch, config_line_t *, wanted,
-      {
-        char *address=NULL;
-        uint16_t port;
-        switch (socket_family) {
-          case AF_INET:
-            if (!parse_addr_port(LOG_WARN,
-                                 wanted->value, &address, NULL, &port)) {
-              int addr_matches = !strcasecmp(address, conn->address);
-              int port_matches;
-              tor_free(address);
-              if (port) {
-                /* The Listener line has a port */
-                port_matches = (port == conn->port);
-              } else if (port_option == CFG_AUTO_PORT) {
-                /* The Listener line has no port, and the Port line is "auto".
-                 * "auto" matches anything; transitions from any port to
-                 * "auto" succeed. */
-                port_matches = 1;
-              } else {
-                /*  The Listener line has no port, and the Port line is "auto".
-                 * "auto" matches anything; transitions from any port to
-                 * "auto" succeed. */
-                port_matches = (port_option == conn->port);
-              }
-              if (port_matches  && addr_matches) {
-                line = wanted;
-                break;
-              }
-            }
-            break;
-          case AF_UNIX:
-            if (!strcasecmp(wanted->value, conn->address)) {
-              line = wanted;
-              break;
-            }
-            break;
-          default:
-            tor_assert(0);
-        }
-      });
-    if (!line || disable_all_conns) {
-      /* This one isn't configured. Close it. */
-      log_notice(LD_NET, "Closing no-longer-configured %s on %s:%d",
-                 conn_type_to_string(type), conn->address, conn->port);
-      if (replaced_conns) {
-        smartlist_add(replaced_conns, conn);
-      } else {
-        connection_close_immediate(conn);
-        connection_mark_for_close(conn);
-      }
+  if (port_option) {
+    if (!cfg) {
+      port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t));
+      tor_addr_copy(&port->addr, &dflt_addr);
+      port->port = port_option;
+      port->type = type;
+      smartlist_add(ports, port);
     } else {
     } else {
-      /* It's configured; we don't need to launch it. */
-//      log_debug(LD_NET, "Already have %s on %s:%d",
-//                conn_type_to_string(type), conn->address, conn->port);
-      smartlist_remove(launch, line);
-      if (free_launch_elts)
-        config_free_lines(line);
-    }
-  });
-
-  /* Now open all the listeners that are configured but not opened. */
-  r = 0;
-  if (!disable_all_conns) {
-    SMARTLIST_FOREACH_BEGIN(launch, config_line_t *, cfg_line) {
-        char *address = NULL;
-        struct sockaddr *listensockaddr;
-        socklen_t listensocklen = 0;
-
-        switch (socket_family) {
-          case AF_INET:
-            listensockaddr = (struct sockaddr *)
-                             create_inet_sockaddr(cfg_line->value,
-                                                  port_option,
-                                                  &address, &listensocklen);
-            break;
-          case AF_UNIX:
-            listensockaddr = (struct sockaddr *)
-                             create_unix_sockaddr(cfg_line->value,
-                                                  &address, &listensocklen);
-            break;
-          default:
-            tor_assert(0);
-        }
-
-        if (listensockaddr) {
-          conn = connection_create_listener(listensockaddr, listensocklen,
-                                            type, address);
-          tor_free(listensockaddr);
-          tor_free(address);
-        } else
-          conn = NULL;
-
-        if (!conn) {
-          r = -1;
+      const config_line_t *c;
+      for (c = cfg; c; c = c->next) {
+        port_cfg_t *port;
+        tor_addr_t addr;
+        uint16_t portval = 0;
+        if (is_sockaddr_un) {
+          size_t len = strlen(c->value);
+          port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1);
+          port->is_unix_addr = 1;
+          memcpy(port->unix_addr, c->value, len+1);
         } else {
         } else {
-          if (new_conns)
-            smartlist_add(new_conns, conn);
+          if (tor_addr_port_parse(c->value, &addr, &portval) < 0) {
+            log_warn(LD_CONFIG, "Can't parse/resolve %s %s",
+                     c->key, c->value);
+            retval = -1;
+            continue;
+          }
+          port = tor_malloc_zero(sizeof(port_cfg_t));
+          tor_addr_copy(&port->addr, &addr);
         }
         }
-    } SMARTLIST_FOREACH_END(cfg_line);
+        port->type = type;
+        port->port = portval ? portval : port_option;
+        smartlist_add(ports, port);
+      }
+    }
   }
   }
 
 
-  if (free_launch_elts) {
-    SMARTLIST_FOREACH(launch, config_line_t *, cfg_line,
-                      config_free_lines(cfg_line));
-  }
-  smartlist_free(launch);
+  if (retval == -1)
+    goto cleanup;
 
 
-  return r;
+  retval = retry_listener_ports(old_conns, ports, new_conns);
+
+ cleanup:
+  SMARTLIST_FOREACH(ports, port_cfg_t *, p, tor_free(p));
+  smartlist_free(ports);
+  return retval;
 }
 }
 
 
 /** Launch listeners for each port you should have open.  Only launch
 /** Launch listeners for each port you should have open.  Only launch
@@ -1923,54 +1909,62 @@ int
 retry_all_listeners(smartlist_t *replaced_conns,
 retry_all_listeners(smartlist_t *replaced_conns,
                     smartlist_t *new_conns)
                     smartlist_t *new_conns)
 {
 {
+  smartlist_t *listeners = smartlist_create();
   const or_options_t *options = get_options();
   const or_options_t *options = get_options();
   int retval = 0;
   int retval = 0;
   const uint16_t old_or_port = router_get_advertised_or_port(options);
   const uint16_t old_or_port = router_get_advertised_or_port(options);
   const uint16_t old_dir_port = router_get_advertised_dir_port(options, 0);
   const uint16_t old_dir_port = router_get_advertised_dir_port(options, 0);
 
 
-  if (retry_listeners(CONN_TYPE_OR_LISTENER, options->ORListenAddress,
-                      options->ORPort, "0.0.0.0",
-                      replaced_conns, new_conns, options->ClientOnly,
-                      AF_INET)<0)
-    retval = -1;
-  if (retry_listeners(CONN_TYPE_DIR_LISTENER, options->DirListenAddress,
-                      options->DirPort, "0.0.0.0",
-                      replaced_conns, new_conns, options->ClientOnly,
-                      AF_INET)<0)
-    retval = -1;
-  if (retry_listeners(CONN_TYPE_AP_LISTENER, options->SocksListenAddress,
-                      options->SocksPort, "127.0.0.1",
-                      replaced_conns, new_conns, 0,
-                      AF_INET)<0)
-    retval = -1;
-  if (retry_listeners(CONN_TYPE_AP_TRANS_LISTENER, options->TransListenAddress,
-                      options->TransPort, "127.0.0.1",
-                      replaced_conns, new_conns, 0,
-                      AF_INET)<0)
-    retval = -1;
-  if (retry_listeners(CONN_TYPE_AP_NATD_LISTENER, options->NATDListenAddress,
-                      options->NATDPort, "127.0.0.1",
-                      replaced_conns, new_conns, 0,
-                      AF_INET)<0)
-    retval = -1;
-  if (retry_listeners(CONN_TYPE_AP_DNS_LISTENER, options->DNSListenAddress,
-                      options->DNSPort, "127.0.0.1",
-                      replaced_conns, new_conns, 0,
-                      AF_INET)<0)
+  SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
+    if (connection_is_listener(conn) && !conn->marked_for_close)
+      smartlist_add(listeners, conn);
+  } SMARTLIST_FOREACH_END(conn);
+
+  if (! options->ClientOnly) {
+    if (retry_listeners(listeners,
+                        CONN_TYPE_OR_LISTENER, options->ORListenAddress,
+                        options->ORPort, "0.0.0.0",
+                        new_conns, 0) < 0)
+      retval = -1;
+    if (retry_listeners(listeners,
+                        CONN_TYPE_DIR_LISTENER, options->DirListenAddress,
+                        options->DirPort, "0.0.0.0",
+                        new_conns, 0) < 0)
+      retval = -1;
+  }
+
+  if (retry_listener_ports(listeners,
+                           get_configured_client_ports(),
+                           new_conns) < 0)
     retval = -1;
     retval = -1;
-  if (retry_listeners(CONN_TYPE_CONTROL_LISTENER,
+  if (retry_listeners(listeners,
+                      CONN_TYPE_CONTROL_LISTENER,
                       options->ControlListenAddress,
                       options->ControlListenAddress,
                       options->ControlPort, "127.0.0.1",
                       options->ControlPort, "127.0.0.1",
-                      replaced_conns, new_conns, 0,
-                      AF_INET)<0)
+                      new_conns, 0) < 0)
     return -1;
     return -1;
-  if (retry_listeners(CONN_TYPE_CONTROL_LISTENER,
+  if (retry_listeners(listeners,
+                      CONN_TYPE_CONTROL_LISTENER,
                       options->ControlSocket,
                       options->ControlSocket,
                       options->ControlSocket ? 1 : 0, NULL,
                       options->ControlSocket ? 1 : 0, NULL,
-                      replaced_conns, new_conns, 0,
-                      AF_UNIX)<0)
+                      new_conns, 1) < 0)
     return -1;
     return -1;
 
 
+  /* Any members that were still in 'listeners' don't correspond to
+   * any configured port.  Kill 'em. */
+  SMARTLIST_FOREACH_BEGIN(listeners, connection_t *, conn) {
+    log_notice(LD_NET, "Closing no-longer-configured %s on %s:%d",
+               conn_type_to_string(conn->type), conn->address, conn->port);
+    if (replaced_conns) {
+      smartlist_add(replaced_conns, conn);
+    } else {
+      connection_close_immediate(conn);
+      connection_mark_for_close(conn);
+    }
+  } SMARTLIST_FOREACH_END(conn);
+
+  smartlist_free(listeners);
+
   if (old_or_port != router_get_advertised_or_port(options) ||
   if (old_or_port != router_get_advertised_or_port(options) ||
       old_dir_port != router_get_advertised_dir_port(options, 0)) {
       old_dir_port != router_get_advertised_dir_port(options, 0)) {
     /* Our chosen ORPort or DirPort is not what it used to be: the
     /* Our chosen ORPort or DirPort is not what it used to be: the
@@ -3910,6 +3904,9 @@ assert_connection_ok(connection_t *conn, time_t now)
     case CONN_TYPE_CONTROL:
     case CONN_TYPE_CONTROL:
       tor_assert(conn->magic == CONTROL_CONNECTION_MAGIC);
       tor_assert(conn->magic == CONTROL_CONNECTION_MAGIC);
       break;
       break;
+    CASE_ANY_LISTENER_TYPE:
+      tor_assert(conn->magic == LISTENER_CONNECTION_MAGIC);
+      break;
     default:
     default:
       tor_assert(conn->magic == BASE_CONNECTION_MAGIC);
       tor_assert(conn->magic == BASE_CONNECTION_MAGIC);
       break;
       break;
@@ -3994,13 +3991,7 @@ assert_connection_ok(connection_t *conn, time_t now)
 
 
   switch (conn->type)
   switch (conn->type)
     {
     {
-    case CONN_TYPE_OR_LISTENER:
-    case CONN_TYPE_AP_LISTENER:
-    case CONN_TYPE_AP_TRANS_LISTENER:
-    case CONN_TYPE_AP_NATD_LISTENER:
-    case CONN_TYPE_DIR_LISTENER:
-    case CONN_TYPE_CONTROL_LISTENER:
-    case CONN_TYPE_AP_DNS_LISTENER:
+    CASE_ANY_LISTENER_TYPE:
       tor_assert(conn->state == LISTENER_STATE_READY);
       tor_assert(conn->state == LISTENER_STATE_READY);
       break;
       break;
     case CONN_TYPE_OR:
     case CONN_TYPE_OR:
@@ -4136,3 +4127,42 @@ proxy_type_to_string(int proxy_type)
   return NULL; /*Unreached*/
   return NULL; /*Unreached*/
 }
 }
 
 
+/** Call _connection_free() on every connection in our array, and release all
+ * storage held by connection.c. This is used by cpuworkers and dnsworkers
+ * when they fork, so they don't keep resources held open (especially
+ * sockets).
+ *
+ * Don't do the checks in connection_free(), because they will
+ * fail.
+ */
+void
+connection_free_all(void)
+{
+  smartlist_t *conns = get_connection_array();
+
+  /* We don't want to log any messages to controllers. */
+  SMARTLIST_FOREACH(conns, connection_t *, conn,
+    if (conn->type == CONN_TYPE_CONTROL)
+      TO_CONTROL_CONN(conn)->event_mask = 0);
+
+  control_update_global_event_mask();
+
+  /* Unlink everything from the identity map. */
+  connection_or_clear_identity_map();
+
+  /* Clear out our list of broken connections */
+  clear_broken_connection_map(0);
+
+  SMARTLIST_FOREACH(conns, connection_t *, conn, _connection_free(conn));
+
+  if (outgoing_addrs) {
+    SMARTLIST_FOREACH(outgoing_addrs, void*, addr, tor_free(addr));
+    smartlist_free(outgoing_addrs);
+    outgoing_addrs = NULL;
+  }
+
+#ifdef USE_BUFFEREVENTS
+  if (global_rate_limit)
+    bufferevent_rate_limit_group_free(global_rate_limit);
+#endif
+}

+ 1 - 0
src/or/connection.h

@@ -22,6 +22,7 @@ dir_connection_t *dir_connection_new(int socket_family);
 or_connection_t *or_connection_new(int socket_family);
 or_connection_t *or_connection_new(int socket_family);
 edge_connection_t *edge_connection_new(int type, int socket_family);
 edge_connection_t *edge_connection_new(int type, int socket_family);
 control_connection_t *control_connection_new(int socket_family);
 control_connection_t *control_connection_new(int socket_family);
+listener_connection_t *listener_connection_new(int type, int socket_family);
 connection_t *connection_new(int type, int socket_family);
 connection_t *connection_new(int type, int socket_family);
 
 
 void connection_link_connections(connection_t *conn_a, connection_t *conn_b);
 void connection_link_connections(connection_t *conn_a, connection_t *conn_b);

+ 270 - 3
src/or/connection_edge.c

@@ -57,6 +57,7 @@ static int connection_exit_connect_dir(edge_connection_t *exitconn);
 static int address_is_in_virtual_range(const char *addr);
 static int address_is_in_virtual_range(const char *addr);
 static int consider_plaintext_ports(edge_connection_t *conn, uint16_t port);
 static int consider_plaintext_ports(edge_connection_t *conn, uint16_t port);
 static void clear_trackexithost_mappings(const char *exitname);
 static void clear_trackexithost_mappings(const char *exitname);
+static int connection_ap_supports_optimistic_data(const edge_connection_t *);
 
 
 /** An AP stream has failed/finished. If it hasn't already sent back
 /** An AP stream has failed/finished. If it hasn't already sent back
  * a socks reply, send one now (based on endreason). Also set
  * a socks reply, send one now (based on endreason). Also set
@@ -154,10 +155,26 @@ connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
         return -1;
         return -1;
       }
       }
       return 0;
       return 0;
+    case AP_CONN_STATE_CONNECT_WAIT:
+      if (connection_ap_supports_optimistic_data(conn)) {
+        log_info(LD_EDGE,
+                 "data from edge while in '%s' state. Sending it anyway. "
+                 "package_partial=%d, buflen=%ld",
+                 conn_state_to_string(conn->_base.type, conn->_base.state),
+                 package_partial,
+                 (long)connection_get_inbuf_len(TO_CONN(conn)));
+        if (connection_edge_package_raw_inbuf(conn, package_partial, NULL)<0) {
+          /* (We already sent an end cell if possible) */
+          connection_mark_for_close(TO_CONN(conn));
+          return -1;
+        }
+        return 0;
+      }
+      /* Fall through if the connection is on a circuit without optimistic
+       * data support. */
     case EXIT_CONN_STATE_CONNECTING:
     case EXIT_CONN_STATE_CONNECTING:
     case AP_CONN_STATE_RENDDESC_WAIT:
     case AP_CONN_STATE_RENDDESC_WAIT:
     case AP_CONN_STATE_CIRCUIT_WAIT:
     case AP_CONN_STATE_CIRCUIT_WAIT:
-    case AP_CONN_STATE_CONNECT_WAIT:
     case AP_CONN_STATE_RESOLVE_WAIT:
     case AP_CONN_STATE_RESOLVE_WAIT:
     case AP_CONN_STATE_CONTROLLER_WAIT:
     case AP_CONN_STATE_CONTROLLER_WAIT:
       log_info(LD_EDGE,
       log_info(LD_EDGE,
@@ -722,6 +739,12 @@ connection_ap_detach_retriable(edge_connection_t *conn, origin_circuit_t *circ,
 {
 {
   control_event_stream_status(conn, STREAM_EVENT_FAILED_RETRIABLE, reason);
   control_event_stream_status(conn, STREAM_EVENT_FAILED_RETRIABLE, reason);
   conn->_base.timestamp_lastread = time(NULL);
   conn->_base.timestamp_lastread = time(NULL);
+
+  if (conn->pending_optimistic_data) {
+    generic_buffer_set_to_copy(&conn->sending_optimistic_data,
+                               conn->pending_optimistic_data);
+  }
+
   if (!get_options()->LeaveStreamsUnattached || conn->use_begindir) {
   if (!get_options()->LeaveStreamsUnattached || conn->use_begindir) {
     /* If we're attaching streams ourself, or if this connection is
     /* If we're attaching streams ourself, or if this connection is
      * a tunneled directory connection, then just attach it. */
      * a tunneled directory connection, then just attach it. */
@@ -1671,6 +1694,9 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
             safe_str_client(socks->address),
             safe_str_client(socks->address),
             socks->port);
             socks->port);
 
 
+  if (! conn->original_dest_address)
+    conn->original_dest_address = tor_strdup(conn->socks_request->address);
+
   if (socks->command == SOCKS_COMMAND_RESOLVE &&
   if (socks->command == SOCKS_COMMAND_RESOLVE &&
       !tor_inet_aton(socks->address, &addr_tmp) &&
       !tor_inet_aton(socks->address, &addr_tmp) &&
       options->AutomapHostsOnResolve && options->AutomapHostsSuffixes) {
       options->AutomapHostsOnResolve && options->AutomapHostsSuffixes) {
@@ -2334,6 +2360,22 @@ get_unique_stream_id_by_circ(origin_circuit_t *circ)
   return test_stream_id;
   return test_stream_id;
 }
 }
 
 
+/** Return true iff <b>conn</b> is linked to a circuit and configured to use
+ * an exit that supports optimistic data. */
+static int
+connection_ap_supports_optimistic_data(const edge_connection_t *conn)
+{
+  tor_assert(conn->_base.type == CONN_TYPE_AP);
+  /* We can only send optimistic data if we're connected to an open
+     general circuit. */
+  if (conn->on_circuit == NULL ||
+      conn->on_circuit->state != CIRCUIT_STATE_OPEN ||
+      conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_GENERAL)
+    return 0;
+
+  return conn->may_use_optimistic_data;
+}
+
 /** Write a relay begin cell, using destaddr and destport from ap_conn's
 /** Write a relay begin cell, using destaddr and destport from ap_conn's
  * socks_request field, and send it down circ.
  * socks_request field, and send it down circ.
  *
  *
@@ -2395,6 +2437,21 @@ connection_ap_handshake_send_begin(edge_connection_t *ap_conn)
   log_info(LD_APP,"Address/port sent, ap socket %d, n_circ_id %d",
   log_info(LD_APP,"Address/port sent, ap socket %d, n_circ_id %d",
            ap_conn->_base.s, circ->_base.n_circ_id);
            ap_conn->_base.s, circ->_base.n_circ_id);
   control_event_stream_status(ap_conn, STREAM_EVENT_SENT_CONNECT, 0);
   control_event_stream_status(ap_conn, STREAM_EVENT_SENT_CONNECT, 0);
+
+  /* If there's queued-up data, send it now */
+  if ((connection_get_inbuf_len(TO_CONN(ap_conn)) ||
+       ap_conn->sending_optimistic_data) &&
+      connection_ap_supports_optimistic_data(ap_conn)) {
+    log_info(LD_APP, "Sending up to %ld + %ld bytes of queued-up data",
+             (long)connection_get_inbuf_len(TO_CONN(ap_conn)),
+             ap_conn->sending_optimistic_data ?
+                 (long)generic_buffer_len(ap_conn->sending_optimistic_data) :
+                 0);
+    if (connection_edge_package_raw_inbuf(ap_conn, 1, NULL) < 0) {
+      connection_mark_for_close(TO_CONN(ap_conn));
+    }
+  }
+
   return 0;
   return 0;
 }
 }
 
 
@@ -2494,7 +2551,9 @@ connection_ap_handshake_send_resolve(edge_connection_t *ap_conn)
 edge_connection_t *
 edge_connection_t *
 connection_ap_make_link(connection_t *partner,
 connection_ap_make_link(connection_t *partner,
                         char *address, uint16_t port,
                         char *address, uint16_t port,
-                        const char *digest, int use_begindir, int want_onehop)
+                        const char *digest,
+                        int session_group, int isolation_flags,
+                        int use_begindir, int want_onehop)
 {
 {
   edge_connection_t *conn;
   edge_connection_t *conn;
 
 
@@ -2524,6 +2583,12 @@ connection_ap_make_link(connection_t *partner,
                   digest, DIGEST_LEN);
                   digest, DIGEST_LEN);
   }
   }
 
 
+  /* Populate isolation fields. */
+  conn->socks_request->listener_type = CONN_TYPE_DIR_LISTENER;
+  conn->original_dest_address = tor_strdup(address);
+  conn->session_group = session_group;
+  conn->isolation_flags = isolation_flags;
+
   conn->_base.address = tor_strdup("(Tor_internal)");
   conn->_base.address = tor_strdup("(Tor_internal)");
   tor_addr_make_unspec(&conn->_base.addr);
   tor_addr_make_unspec(&conn->_base.addr);
   conn->_base.port = 0;
   conn->_base.port = 0;
@@ -3166,7 +3231,7 @@ connection_edge_is_rendezvous_stream(edge_connection_t *conn)
  * resolved.)
  * resolved.)
  */
  */
 int
 int
-connection_ap_can_use_exit(edge_connection_t *conn, const node_t *exit)
+connection_ap_can_use_exit(const edge_connection_t *conn, const node_t *exit)
 {
 {
   const or_options_t *options = get_options();
   const or_options_t *options = get_options();
 
 
@@ -3266,3 +3331,205 @@ parse_extended_hostname(char *address, int allowdotexit)
     return BAD_HOSTNAME;
     return BAD_HOSTNAME;
 }
 }
 
 
+/** Return true iff the (possibly NULL) <b>alen</b>-byte chunk of memory at
+ * <b>a</b> is equal to the (possibly NULL) <b>blen</b>-byte chunk of memory
+ * at <b>b</b>. */
+static int
+memeq_opt(const char *a, size_t alen, const char *b, size_t blen)
+{
+  if (a == NULL) {
+    return (b == NULL);
+  } else if (b == NULL) {
+    return 0;
+  } else if (alen != blen) {
+    return 0;
+  } else {
+    return tor_memeq(a, b, alen);
+  }
+}
+
+/**
+ * Return true iff none of the isolation flags and fields in <b>conn</b>
+ * should prevent it from being attached to <b>circ</b>.
+ */
+int
+connection_edge_compatible_with_circuit(const edge_connection_t *conn,
+                                        const origin_circuit_t *circ)
+{
+  const uint8_t iso = conn->isolation_flags;
+  const socks_request_t *sr = conn->socks_request;
+
+  /* If circ has never been used for an isolated connection, we can
+   * totally use it for this one. */
+  if (!circ->isolation_values_set)
+    return 1;
+
+  /* If circ has been used for connections having more than one value
+   * for some field f, it will have the corresponding bit set in
+   * isolation_flags_mixed.  If isolation_flags_mixed has any bits
+   * in common with iso, then conn must be isolated from at least
+   * one stream that has been attached to circ. */
+  if ((iso & circ->isolation_flags_mixed) != 0) {
+    /* For at least one field where conn is isolated, the circuit
+     * already has mixed streams. */
+    return 0;
+  }
+
+  if (! conn->original_dest_address) {
+    log_warn(LD_BUG, "Reached connection_edge_compatible_with_circuit without "
+             "having set conn->original_dest_address");
+    ((edge_connection_t*)conn)->original_dest_address =
+      tor_strdup(conn->socks_request->address);
+  }
+
+  if ((iso & ISO_STREAM) &&
+      (circ->associated_isolated_stream_global_id !=
+       TO_CONN(conn)->global_identifier))
+    return 0;
+
+  if ((iso & ISO_DESTPORT) && conn->socks_request->port != circ->dest_port)
+    return 0;
+  if ((iso & ISO_DESTADDR) &&
+      strcasecmp(conn->original_dest_address, circ->dest_address))
+    return 0;
+  if ((iso & ISO_SOCKSAUTH) &&
+      (! memeq_opt(sr->username, sr->usernamelen,
+                   circ->socks_username, circ->socks_username_len) ||
+       ! memeq_opt(sr->password, sr->passwordlen,
+                   circ->socks_password, circ->socks_password_len)))
+    return 0;
+  if ((iso & ISO_CLIENTPROTO) &&
+      (conn->socks_request->listener_type != circ->client_proto_type ||
+       conn->socks_request->socks_version != circ->client_proto_socksver))
+    return 0;
+  if ((iso & ISO_CLIENTADDR) &&
+      !tor_addr_eq(&TO_CONN(conn)->addr, &circ->client_addr))
+    return 0;
+  if ((iso & ISO_SESSIONGRP) && conn->session_group != circ->session_group)
+    return 0;
+  if ((iso & ISO_NYM_EPOCH) && conn->nym_epoch != circ->nym_epoch)
+    return 0;
+
+  return 1;
+}
+
+/**
+ * If <b>dry_run</b> is false, update <b>circ</b>'s isolation flags and fields
+ * to reflect having had <b>conn</b> attached to it, and return 0.  Otherwise,
+ * if <b>dry_run</b> is true, then make no changes to <b>circ</b>, and return
+ * a bitfield of isolation flags that we would have to set in
+ * isolation_flags_mixed to add <b>conn</b> to <b>circ</b>, or -1 if
+ * <b>circ</b> has had no streams attached to it.
+ */
+int
+connection_edge_update_circuit_isolation(const edge_connection_t *conn,
+                                         origin_circuit_t *circ,
+                                         int dry_run)
+{
+  const socks_request_t *sr = conn->socks_request;
+  if (! conn->original_dest_address) {
+    log_warn(LD_BUG, "Reached connection_update_circuit_isolation without "
+             "having set conn->original_dest_address");
+    ((edge_connection_t*)conn)->original_dest_address =
+      tor_strdup(conn->socks_request->address);
+  }
+
+  if (!circ->isolation_values_set) {
+    if (dry_run)
+      return -1;
+    circ->associated_isolated_stream_global_id =
+      TO_CONN(conn)->global_identifier;
+    circ->dest_port = conn->socks_request->port;
+    circ->dest_address = tor_strdup(conn->original_dest_address);
+    circ->client_proto_type = conn->socks_request->listener_type;
+    circ->client_proto_socksver = conn->socks_request->socks_version;
+    tor_addr_copy(&circ->client_addr, &TO_CONN(conn)->addr);
+    circ->session_group = conn->session_group;
+    circ->nym_epoch = conn->nym_epoch;
+    circ->socks_username = sr->username ?
+      tor_memdup(sr->username, sr->usernamelen) : NULL;
+    circ->socks_password = sr->password ?
+      tor_memdup(sr->password, sr->passwordlen) : NULL;
+    circ->socks_username_len = sr->usernamelen;
+    circ->socks_password_len = sr->passwordlen;
+
+    circ->isolation_values_set = 1;
+    return 0;
+  } else {
+    uint8_t mixed = 0;
+    if (conn->socks_request->port != circ->dest_port)
+      mixed |= ISO_DESTPORT;
+    if (strcasecmp(conn->original_dest_address, circ->dest_address))
+      mixed |= ISO_DESTADDR;
+    if (!memeq_opt(sr->username, sr->usernamelen,
+                   circ->socks_username, circ->socks_username_len) ||
+        !memeq_opt(sr->password, sr->passwordlen,
+                   circ->socks_password, circ->socks_password_len))
+      mixed |= ISO_SOCKSAUTH;
+    if ((conn->socks_request->listener_type != circ->client_proto_type ||
+         conn->socks_request->socks_version != circ->client_proto_socksver))
+      mixed |= ISO_CLIENTPROTO;
+    if (!tor_addr_eq(&TO_CONN(conn)->addr, &circ->client_addr))
+      mixed |= ISO_CLIENTADDR;
+    if (conn->session_group != circ->session_group)
+      mixed |= ISO_SESSIONGRP;
+    if (conn->nym_epoch != circ->nym_epoch)
+      mixed |= ISO_NYM_EPOCH;
+
+    if (dry_run)
+      return mixed;
+
+    if ((mixed & conn->isolation_flags) != 0) {
+      log_warn(LD_BUG, "Updating a circuit with seemingly incompatible "
+               "isolation flags.");
+    }
+    circ->isolation_flags_mixed |= mixed;
+    return 0;
+  }
+}
+
+/**
+ * Clear the isolation settings on <b>circ</b>.
+ *
+ * This only works on an open circuit that has never had a stream attached to
+ * it, and whose isolation settings are hypothetical.  (We set hypothetical
+ * isolation settings on circuits as we're launching them, so that we
+ * know whether they can handle more streams or whether we need to launch
+ * even more circuits.  Once the circuit is open, if it turns out that
+ * we no longer have any streams to attach to it, we clear the isolation flags
+ * and data so that other streams can have a chance.)
+ */
+void
+circuit_clear_isolation(origin_circuit_t *circ)
+{
+  if (circ->isolation_any_streams_attached) {
+    log_warn(LD_BUG, "Tried to clear the isolation status of a dirty circuit");
+    return;
+  }
+  if (TO_CIRCUIT(circ)->state != CIRCUIT_STATE_OPEN) {
+    log_warn(LD_BUG, "Tried to clear the isolation status of a non-open "
+             "circuit");
+    return;
+  }
+
+  circ->isolation_values_set = 0;
+  circ->isolation_flags_mixed = 0;
+  circ->associated_isolated_stream_global_id = 0;
+  circ->client_proto_type = 0;
+  circ->client_proto_socksver = 0;
+  circ->dest_port = 0;
+  tor_addr_make_unspec(&circ->client_addr);
+  tor_free(circ->dest_address);
+  circ->session_group = -1;
+  circ->nym_epoch = 0;
+  if (circ->socks_username) {
+    memset(circ->socks_username, 0x11, circ->socks_username_len);
+    tor_free(circ->socks_username);
+  }
+  if (circ->socks_password) {
+    memset(circ->socks_password, 0x05, circ->socks_password_len);
+    tor_free(circ->socks_password);
+  }
+  circ->socks_username_len = circ->socks_password_len = 0;
+}
+

+ 10 - 1
src/or/connection_edge.h

@@ -36,6 +36,8 @@ int connection_ap_handshake_send_resolve(edge_connection_t *ap_conn);
 edge_connection_t  *connection_ap_make_link(connection_t *partner,
 edge_connection_t  *connection_ap_make_link(connection_t *partner,
                                             char *address, uint16_t port,
                                             char *address, uint16_t port,
                                             const char *digest,
                                             const char *digest,
+                                            int session_group,
+                                            int isolation_flags,
                                             int use_begindir, int want_onehop);
                                             int use_begindir, int want_onehop);
 void connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
 void connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
                                          size_t replylen,
                                          size_t replylen,
@@ -51,7 +53,7 @@ int connection_exit_begin_conn(cell_t *cell, circuit_t *circ);
 int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ);
 int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ);
 void connection_exit_connect(edge_connection_t *conn);
 void connection_exit_connect(edge_connection_t *conn);
 int connection_edge_is_rendezvous_stream(edge_connection_t *conn);
 int connection_edge_is_rendezvous_stream(edge_connection_t *conn);
-int connection_ap_can_use_exit(edge_connection_t *conn,
+int connection_ap_can_use_exit(const edge_connection_t *conn,
                                const node_t *exit);
                                const node_t *exit);
 void connection_ap_expire_beginning(void);
 void connection_ap_expire_beginning(void);
 void connection_ap_attach_pending(void);
 void connection_ap_attach_pending(void);
@@ -103,5 +105,12 @@ hostname_type_t parse_extended_hostname(char *address, int allowdotexit);
 int get_pf_socket(void);
 int get_pf_socket(void);
 #endif
 #endif
 
 
+int connection_edge_compatible_with_circuit(const edge_connection_t *conn,
+                                            const origin_circuit_t *circ);
+int connection_edge_update_circuit_isolation(const edge_connection_t *conn,
+                                             origin_circuit_t *circ,
+                                             int dry_run);
+void circuit_clear_isolation(origin_circuit_t *circ);
+
 #endif
 #endif
 
 

+ 37 - 2
src/or/control.c

@@ -74,7 +74,8 @@
 #define EVENT_NEWCONSENSUS     0x0016
 #define EVENT_NEWCONSENSUS     0x0016
 #define EVENT_BUILDTIMEOUT_SET     0x0017
 #define EVENT_BUILDTIMEOUT_SET     0x0017
 #define EVENT_SIGNAL           0x0018
 #define EVENT_SIGNAL           0x0018
-#define _EVENT_MAX             0x0018
+#define EVENT_CONF_CHANGED     0x0019
+#define _EVENT_MAX             0x0019
 /* If _EVENT_MAX ever hits 0x0020, we need to make the mask wider. */
 /* If _EVENT_MAX ever hits 0x0020, we need to make the mask wider. */
 
 
 /** Bitfield: The bit 1&lt;&lt;e is set if <b>any</b> open control
 /** Bitfield: The bit 1&lt;&lt;e is set if <b>any</b> open control
@@ -946,6 +947,7 @@ static const struct control_event_t control_event_table[] = {
   { EVENT_NEWCONSENSUS, "NEWCONSENSUS" },
   { EVENT_NEWCONSENSUS, "NEWCONSENSUS" },
   { EVENT_BUILDTIMEOUT_SET, "BUILDTIMEOUT_SET" },
   { EVENT_BUILDTIMEOUT_SET, "BUILDTIMEOUT_SET" },
   { EVENT_SIGNAL, "SIGNAL" },
   { EVENT_SIGNAL, "SIGNAL" },
+  { EVENT_CONF_CHANGED, "CONF_CHANGED"},
   { 0, NULL },
   { 0, NULL },
 };
 };
 
 
@@ -3567,7 +3569,7 @@ control_event_logmsg(int severity, uint32_t domain, const char *msg)
       severity <= LOG_NOTICE) {
       severity <= LOG_NOTICE) {
     char *esc = esc_for_log(msg);
     char *esc = esc_for_log(msg);
     ++disable_log_messages;
     ++disable_log_messages;
-    control_event_general_status(severity, "BUG REASON=\"%s\"", esc);
+    control_event_general_status(severity, "BUG REASON=%s", esc);
     --disable_log_messages;
     --disable_log_messages;
     tor_free(esc);
     tor_free(esc);
   }
   }
@@ -3996,6 +3998,39 @@ control_event_guard(const char *nickname, const char *digest,
   return 0;
   return 0;
 }
 }
 
 
+/** Called when a configuration option changes. This is generally triggered
+ * by SETCONF requests and RELOAD/SIGHUP signals. The <b>elements</b> is
+ * a smartlist_t containing (key, value, ...) pairs in sequence.
+ * <b>value</b> can be NULL. */
+int
+control_event_conf_changed(smartlist_t *elements)
+{
+  int i;
+  char *result;
+  smartlist_t *lines;
+  if (!EVENT_IS_INTERESTING(EVENT_CONF_CHANGED) ||
+      smartlist_len(elements) == 0) {
+    return 0;
+  }
+  lines = smartlist_create();
+  for (i = 0; i < smartlist_len(elements); i += 2) {
+    char *k = smartlist_get(elements, i);
+    char *v = smartlist_get(elements, i+1);
+    if (v == NULL) {
+      smartlist_asprintf_add(lines, "650-%s", k);
+    } else {
+      smartlist_asprintf_add(lines, "650-%s=%s", k, v);
+    }
+  }
+  result = smartlist_join_strings(lines, "\r\n", 0, NULL);
+  send_control_event(EVENT_CONF_CHANGED, 0,
+    "650-CONF_CHANGED\r\n%s\r\n650 OK\r\n", result);
+  tor_free(result);
+  SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
+  smartlist_free(lines);
+  return 0;
+}
+
 /** Helper: Return a newly allocated string containing a path to the
 /** Helper: Return a newly allocated string containing a path to the
  * file where we store our authentication cookie. */
  * file where we store our authentication cookie. */
 static char *
 static char *

+ 1 - 0
src/or/control.h

@@ -66,6 +66,7 @@ int control_event_server_status(int severity, const char *format, ...)
   CHECK_PRINTF(2,3);
   CHECK_PRINTF(2,3);
 int control_event_guard(const char *nickname, const char *digest,
 int control_event_guard(const char *nickname, const char *digest,
                         const char *status);
                         const char *status);
+int control_event_conf_changed(smartlist_t *elements);
 int control_event_buildtimeout_set(const circuit_build_times_t *cbt,
 int control_event_buildtimeout_set(const circuit_build_times_t *cbt,
                                    buildtimeout_set_event_t type);
                                    buildtimeout_set_event_t type);
 int control_event_signal(uintptr_t signal);
 int control_event_signal(uintptr_t signal);

+ 7 - 1
src/or/directory.c

@@ -973,6 +973,10 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
     }
     }
   } else { /* we want to connect via a tor connection */
   } else { /* we want to connect via a tor connection */
     edge_connection_t *linked_conn;
     edge_connection_t *linked_conn;
+    /* Anonymized tunneled connections can never share a circuit.
+     * One-hop directory connections can share circuits with each other
+     * but nothing else. */
+    int iso_flags = anonymized_connection ? ISO_STREAM : ISO_SESSIONGRP;
 
 
     /* If it's an anonymized connection, remember the fact that we
     /* If it's an anonymized connection, remember the fact that we
      * wanted it for later: maybe we'll want it again soon. */
      * wanted it for later: maybe we'll want it again soon. */
@@ -988,7 +992,9 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
     linked_conn =
     linked_conn =
       connection_ap_make_link(TO_CONN(conn),
       connection_ap_make_link(TO_CONN(conn),
                               conn->_base.address, conn->_base.port,
                               conn->_base.address, conn->_base.port,
-                              digest, use_begindir, conn->dirconn_direct);
+                              digest,
+                              SESSION_GROUP_DIRCONN, iso_flags,
+                              use_begindir, conn->dirconn_direct);
     if (!linked_conn) {
     if (!linked_conn) {
       log_warn(LD_NET,"Making tunnel to dirserver failed.");
       log_warn(LD_NET,"Making tunnel to dirserver failed.");
       connection_mark_for_close(TO_CONN(conn));
       connection_mark_for_close(TO_CONN(conn));

+ 24 - 7
src/or/dnsserv.c

@@ -29,8 +29,9 @@
  * DNSPort.  We need to eventually answer the request <b>req</b>.
  * DNSPort.  We need to eventually answer the request <b>req</b>.
  */
  */
 static void
 static void
-evdns_server_callback(struct evdns_server_request *req, void *_data)
+evdns_server_callback(struct evdns_server_request *req, void *data_)
 {
 {
+  const listener_connection_t *listener = data_;
   edge_connection_t *conn;
   edge_connection_t *conn;
   int i = 0;
   int i = 0;
   struct evdns_server_question *q = NULL;
   struct evdns_server_question *q = NULL;
@@ -43,7 +44,7 @@ evdns_server_callback(struct evdns_server_request *req, void *_data)
   char *q_name;
   char *q_name;
 
 
   tor_assert(req);
   tor_assert(req);
-  tor_assert(_data == NULL);
+
   log_info(LD_APP, "Got a new DNS request!");
   log_info(LD_APP, "Got a new DNS request!");
 
 
   req->flags |= 0x80; /* set RA */
   req->flags |= 0x80; /* set RA */
@@ -130,7 +131,11 @@ evdns_server_callback(struct evdns_server_request *req, void *_data)
   strlcpy(conn->socks_request->address, q->name,
   strlcpy(conn->socks_request->address, q->name,
           sizeof(conn->socks_request->address));
           sizeof(conn->socks_request->address));
 
 
+  conn->socks_request->listener_type = listener->_base.type;
   conn->dns_server_request = req;
   conn->dns_server_request = req;
+  conn->isolation_flags = listener->isolation_flags;
+  conn->session_group = listener->session_group;
+  conn->nym_epoch = get_signewnym_epoch();
 
 
   if (connection_add(TO_CONN(conn)) < 0) {
   if (connection_add(TO_CONN(conn)) < 0) {
     log_warn(LD_APP, "Couldn't register dummy connection for DNS request");
     log_warn(LD_APP, "Couldn't register dummy connection for DNS request");
@@ -181,6 +186,12 @@ dnsserv_launch_request(const char *name, int reverse)
   strlcpy(conn->socks_request->address, name,
   strlcpy(conn->socks_request->address, name,
           sizeof(conn->socks_request->address));
           sizeof(conn->socks_request->address));
 
 
+  conn->socks_request->listener_type = CONN_TYPE_CONTROL_LISTENER;
+  conn->original_dest_address = tor_strdup(name);
+  conn->session_group = SESSION_GROUP_CONTROL_RESOLVE;
+  conn->nym_epoch = get_signewnym_epoch();
+  conn->isolation_flags = ISO_DEFAULT;
+
   if (connection_add(TO_CONN(conn))<0) {
   if (connection_add(TO_CONN(conn))<0) {
     log_warn(LD_APP, "Couldn't register dummy connection for RESOLVE request");
     log_warn(LD_APP, "Couldn't register dummy connection for RESOLVE request");
     connection_free(TO_CONN(conn));
     connection_free(TO_CONN(conn));
@@ -305,12 +316,15 @@ dnsserv_resolved(edge_connection_t *conn,
 void
 void
 dnsserv_configure_listener(connection_t *conn)
 dnsserv_configure_listener(connection_t *conn)
 {
 {
+  listener_connection_t *listener_conn;
   tor_assert(conn);
   tor_assert(conn);
   tor_assert(SOCKET_OK(conn->s));
   tor_assert(SOCKET_OK(conn->s));
   tor_assert(conn->type == CONN_TYPE_AP_DNS_LISTENER);
   tor_assert(conn->type == CONN_TYPE_AP_DNS_LISTENER);
 
 
-  conn->dns_server_port =
-    tor_evdns_add_server_port(conn->s, 0, evdns_server_callback, NULL);
+  listener_conn = TO_LISTENER_CONN(conn);
+  listener_conn->dns_server_port =
+    tor_evdns_add_server_port(conn->s, 0, evdns_server_callback,
+                              listener_conn);
 }
 }
 
 
 /** Free the evdns server port for <b>conn</b>, which must be an
 /** Free the evdns server port for <b>conn</b>, which must be an
@@ -318,12 +332,15 @@ dnsserv_configure_listener(connection_t *conn)
 void
 void
 dnsserv_close_listener(connection_t *conn)
 dnsserv_close_listener(connection_t *conn)
 {
 {
+  listener_connection_t *listener_conn;
   tor_assert(conn);
   tor_assert(conn);
   tor_assert(conn->type == CONN_TYPE_AP_DNS_LISTENER);
   tor_assert(conn->type == CONN_TYPE_AP_DNS_LISTENER);
 
 
-  if (conn->dns_server_port) {
-    evdns_close_server_port(conn->dns_server_port);
-    conn->dns_server_port = NULL;
+  listener_conn = TO_LISTENER_CONN(conn);
+
+  if (listener_conn->dns_server_port) {
+    evdns_close_server_port(listener_conn->dns_server_port);
+    listener_conn->dns_server_port = NULL;
   }
   }
 }
 }
 
 

+ 184 - 124
src/or/geoip.c

@@ -930,10 +930,9 @@ geoip_dirreq_stats_init(time_t now)
   start_of_dirreq_stats_interval = now;
   start_of_dirreq_stats_interval = now;
 }
 }
 
 
-/** Stop collecting directory request stats in a way that we can re-start
- * doing so in geoip_dirreq_stats_init(). */
+/** Reset counters for dirreq stats. */
 void
 void
-geoip_dirreq_stats_term(void)
+geoip_reset_dirreq_stats(time_t now)
 {
 {
   SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, {
   SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, {
       c->n_v2_ns_requests = c->n_v3_ns_requests = 0;
       c->n_v2_ns_requests = c->n_v3_ns_requests = 0;
@@ -965,59 +964,41 @@ geoip_dirreq_stats_term(void)
       tor_free(this);
       tor_free(this);
     }
     }
   }
   }
-  start_of_dirreq_stats_interval = 0;
+  start_of_dirreq_stats_interval = now;
 }
 }
 
 
-/** Write dirreq statistics to $DATADIR/stats/dirreq-stats and return when
- * we would next want to write. */
-time_t
-geoip_dirreq_stats_write(time_t now)
+/** Stop collecting directory request stats in a way that we can re-start
+ * doing so in geoip_dirreq_stats_init(). */
+void
+geoip_dirreq_stats_term(void)
 {
 {
-  char *statsdir = NULL, *filename = NULL;
-  char *data_v2 = NULL, *data_v3 = NULL;
-  char written[ISO_TIME_LEN+1];
-  open_file_t *open_file = NULL;
+  geoip_reset_dirreq_stats(0);
+}
+
+/** Return a newly allocated string containing the dirreq statistics
+ * until <b>now</b>, or NULL if we're not collecting dirreq stats. */
+char *
+geoip_format_dirreq_stats(time_t now)
+{
+  char t[ISO_TIME_LEN+1];
   double v2_share = 0.0, v3_share = 0.0;
   double v2_share = 0.0, v3_share = 0.0;
-  FILE *out;
   int i;
   int i;
+  char *v3_ips_string, *v2_ips_string, *v3_reqs_string, *v2_reqs_string,
+       *v2_share_string = NULL, *v3_share_string = NULL,
+       *v3_direct_dl_string, *v2_direct_dl_string,
+       *v3_tunneled_dl_string, *v2_tunneled_dl_string;
+  char *result;
 
 
   if (!start_of_dirreq_stats_interval)
   if (!start_of_dirreq_stats_interval)
-    return 0; /* Not initialized. */
-  if (start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL > now)
-    goto done; /* Not ready to write. */
+    return NULL; /* Not initialized. */
 
 
-  /* 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, get_options()->User) < 0)
-    goto done;
-  filename = get_datadir_fname2("stats", "dirreq-stats");
-  data_v2 = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS_V2);
-  data_v3 = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS);
-  format_iso_time(written, now);
-  out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND | O_TEXT,
-                                    0600, &open_file);
-  if (!out)
-    goto done;
-  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);
+  format_iso_time(t, now);
+  v2_ips_string = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS_V2);
+  v3_ips_string = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS);
+  v2_reqs_string = geoip_get_request_history(
+                   GEOIP_CLIENT_NETWORKSTATUS_V2);
+  v3_reqs_string = geoip_get_request_history(GEOIP_CLIENT_NETWORKSTATUS);
 
 
-  data_v2 = geoip_get_request_history(GEOIP_CLIENT_NETWORKSTATUS_V2);
-  data_v3 = geoip_get_request_history(GEOIP_CLIENT_NETWORKSTATUS);
-  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);
-  SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, {
-      c->n_v2_ns_requests = c->n_v3_ns_requests = 0;
-  });
 #define RESPONSE_GRANULARITY 8
 #define RESPONSE_GRANULARITY 8
   for (i = 0; i < GEOIP_NS_RESPONSE_NUM; i++) {
   for (i = 0; i < GEOIP_NS_RESPONSE_NUM; i++) {
     ns_v2_responses[i] = round_uint32_to_next_multiple_of(
     ns_v2_responses[i] = round_uint32_to_next_multiple_of(
@@ -1026,61 +1007,117 @@ geoip_dirreq_stats_write(time_t now)
                                ns_v3_responses[i], RESPONSE_GRANULARITY);
                                ns_v3_responses[i], RESPONSE_GRANULARITY);
   }
   }
 #undef RESPONSE_GRANULARITY
 #undef RESPONSE_GRANULARITY
-  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],
-                   ns_v3_responses[GEOIP_REJECT_UNAVAILABLE],
-                   ns_v3_responses[GEOIP_REJECT_NOT_FOUND],
-                   ns_v3_responses[GEOIP_REJECT_NOT_MODIFIED],
-                   ns_v3_responses[GEOIP_REJECT_BUSY]) < 0)
-    goto done;
-  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],
-                   ns_v2_responses[GEOIP_REJECT_NOT_FOUND],
-                   ns_v2_responses[GEOIP_REJECT_NOT_MODIFIED],
-                   ns_v2_responses[GEOIP_REJECT_BUSY]) < 0)
-    goto done;
-  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 (!geoip_get_mean_shares(now, &v2_share, &v3_share)) {
-    if (fprintf(out, "dirreq-v2-share %0.2lf%%\n", v2_share*100) < 0)
-      goto done;
-    if (fprintf(out, "dirreq-v3-share %0.2lf%%\n", v3_share*100) < 0)
-      goto done;
+    tor_asprintf(&v2_share_string, "dirreq-v2-share %0.2lf%%\n",
+                 v2_share*100);
+    tor_asprintf(&v3_share_string, "dirreq-v3-share %0.2lf%%\n",
+                 v3_share*100);
   }
   }
 
 
-  data_v2 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS_V2,
-                                       DIRREQ_DIRECT);
-  data_v3 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS,
-                                       DIRREQ_DIRECT);
-  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);
-  tor_free(data_v3);
-  data_v2 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS_V2,
-                                       DIRREQ_TUNNELED);
-  data_v3 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS,
-                                       DIRREQ_TUNNELED);
-  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;
+  v2_direct_dl_string = geoip_get_dirreq_history(
+                        GEOIP_CLIENT_NETWORKSTATUS_V2, DIRREQ_DIRECT);
+  v3_direct_dl_string = geoip_get_dirreq_history(
+                        GEOIP_CLIENT_NETWORKSTATUS, DIRREQ_DIRECT);
+
+  v2_tunneled_dl_string = geoip_get_dirreq_history(
+                          GEOIP_CLIENT_NETWORKSTATUS_V2, DIRREQ_TUNNELED);
+  v3_tunneled_dl_string = geoip_get_dirreq_history(
+                          GEOIP_CLIENT_NETWORKSTATUS, DIRREQ_TUNNELED);
+
+  /* Put everything together into a single string. */
+  tor_asprintf(&result, "dirreq-stats-end %s (%d s)\n"
+              "dirreq-v3-ips %s\n"
+              "dirreq-v2-ips %s\n"
+              "dirreq-v3-reqs %s\n"
+              "dirreq-v2-reqs %s\n"
+              "dirreq-v3-resp ok=%u,not-enough-sigs=%u,unavailable=%u,"
+                   "not-found=%u,not-modified=%u,busy=%u\n"
+              "dirreq-v2-resp ok=%u,unavailable=%u,"
+                   "not-found=%u,not-modified=%u,busy=%u\n"
+              "%s"
+              "%s"
+              "dirreq-v3-direct-dl %s\n"
+              "dirreq-v2-direct-dl %s\n"
+              "dirreq-v3-tunneled-dl %s\n"
+              "dirreq-v2-tunneled-dl %s\n",
+              t,
+              (unsigned) (now - start_of_dirreq_stats_interval),
+              v3_ips_string ? v3_ips_string : "",
+              v2_ips_string ? v2_ips_string : "",
+              v3_reqs_string ? v3_reqs_string : "",
+              v2_reqs_string ? v2_reqs_string : "",
+              ns_v3_responses[GEOIP_SUCCESS],
+              ns_v3_responses[GEOIP_REJECT_NOT_ENOUGH_SIGS],
+              ns_v3_responses[GEOIP_REJECT_UNAVAILABLE],
+              ns_v3_responses[GEOIP_REJECT_NOT_FOUND],
+              ns_v3_responses[GEOIP_REJECT_NOT_MODIFIED],
+              ns_v3_responses[GEOIP_REJECT_BUSY],
+              ns_v2_responses[GEOIP_SUCCESS],
+              ns_v2_responses[GEOIP_REJECT_UNAVAILABLE],
+              ns_v2_responses[GEOIP_REJECT_NOT_FOUND],
+              ns_v2_responses[GEOIP_REJECT_NOT_MODIFIED],
+              ns_v2_responses[GEOIP_REJECT_BUSY],
+              v2_share_string ? v2_share_string : "",
+              v3_share_string ? v3_share_string : "",
+              v3_direct_dl_string ? v3_direct_dl_string : "",
+              v2_direct_dl_string ? v2_direct_dl_string : "",
+              v3_tunneled_dl_string ? v3_tunneled_dl_string : "",
+              v2_tunneled_dl_string ? v2_tunneled_dl_string : "");
+
+  /* Free partial strings. */
+  tor_free(v3_ips_string);
+  tor_free(v2_ips_string);
+  tor_free(v3_reqs_string);
+  tor_free(v2_reqs_string);
+  tor_free(v2_share_string);
+  tor_free(v3_share_string);
+  tor_free(v3_direct_dl_string);
+  tor_free(v2_direct_dl_string);
+  tor_free(v3_tunneled_dl_string);
+  tor_free(v2_tunneled_dl_string);
 
 
-  finish_writing_to_file(open_file);
-  open_file = NULL;
+  return result;
+}
 
 
-  start_of_dirreq_stats_interval = now;
+/** If 24 hours have passed since the beginning of the current dirreq
+ * stats period, write dirreq stats to $DATADIR/stats/dirreq-stats
+ * (possibly overwriting an existing file) and reset counters.  Return
+ * when we would next want to write dirreq stats or 0 if we never want to
+ * write. */
+time_t
+geoip_dirreq_stats_write(time_t now)
+{
+  char *statsdir = NULL, *filename = NULL, *str = NULL;
+
+  if (!start_of_dirreq_stats_interval)
+    return 0; /* Not initialized. */
+  if (start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL > now)
+    goto done; /* Not ready to write. */
+
+  /* Discard all items in the client history that are too old. */
+  geoip_remove_old_clients(start_of_dirreq_stats_interval);
+
+  /* Generate history string .*/
+  str = geoip_format_dirreq_stats(now);
+
+  /* Write dirreq-stats string to disk. */
+  statsdir = get_datadir_fname("stats");
+  if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
+    log_warn(LD_HIST, "Unable to create stats/ directory!");
+    goto done;
+  }
+  filename = get_datadir_fname2("stats", "dirreq-stats");
+  if (write_str_to_file(filename, str, 0) < 0)
+    log_warn(LD_HIST, "Unable to write dirreq statistics to disk!");
+
+  /* Reset measurement interval start. */
+  geoip_reset_dirreq_stats(now);
 
 
  done:
  done:
-  if (open_file)
-    abort_writing_to_file(open_file);
-  tor_free(filename);
   tor_free(statsdir);
   tor_free(statsdir);
-  tor_free(data_v2);
-  tor_free(data_v3);
+  tor_free(filename);
+  tor_free(str);
   return start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL;
   return start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL;
 }
 }
 
 
@@ -1160,8 +1197,8 @@ static char *bridge_stats_extrainfo = NULL;
 /** Return a newly allocated string holding our bridge usage stats by country
 /** Return a newly allocated string holding our bridge usage stats by country
  * in a format suitable for inclusion in an extrainfo document. Return NULL on
  * in a format suitable for inclusion in an extrainfo document. Return NULL on
  * failure.  */
  * failure.  */
-static char *
-format_bridge_stats_extrainfo(time_t now)
+char *
+geoip_format_bridge_stats(time_t now)
 {
 {
   char *out = NULL, *data = NULL;
   char *out = NULL, *data = NULL;
   long duration = now - start_of_bridge_stats_interval;
   long duration = now - start_of_bridge_stats_interval;
@@ -1169,6 +1206,8 @@ format_bridge_stats_extrainfo(time_t now)
 
 
   if (duration < 0)
   if (duration < 0)
     return NULL;
     return NULL;
+  if (!start_of_bridge_stats_interval)
+    return NULL; /* Not initialized. */
 
 
   format_iso_time(written, now);
   format_iso_time(written, now);
   data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
   data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
@@ -1218,7 +1257,7 @@ geoip_bridge_stats_write(time_t now)
   geoip_remove_old_clients(start_of_bridge_stats_interval);
   geoip_remove_old_clients(start_of_bridge_stats_interval);
 
 
   /* Generate formatted string */
   /* Generate formatted string */
-  val = format_bridge_stats_extrainfo(now);
+  val = geoip_format_bridge_stats(now);
   if (val == NULL)
   if (val == NULL)
     goto done;
     goto done;
 
 
@@ -1295,25 +1334,51 @@ geoip_entry_stats_init(time_t now)
   start_of_entry_stats_interval = now;
   start_of_entry_stats_interval = now;
 }
 }
 
 
+/** Reset counters for entry stats. */
+void
+geoip_reset_entry_stats(time_t now)
+{
+  client_history_clear();
+  start_of_entry_stats_interval = now;
+}
+
 /** Stop collecting entry stats in a way that we can re-start doing so in
 /** Stop collecting entry stats in a way that we can re-start doing so in
  * geoip_entry_stats_init(). */
  * geoip_entry_stats_init(). */
 void
 void
 geoip_entry_stats_term(void)
 geoip_entry_stats_term(void)
 {
 {
-  client_history_clear();
-  start_of_entry_stats_interval = 0;
+  geoip_reset_entry_stats(0);
 }
 }
 
 
-/** Write entry statistics to $DATADIR/stats/entry-stats and return time
- * when we would next want to write. */
+/** Return a newly allocated string containing the entry statistics
+ * until <b>now</b>, or NULL if we're not collecting entry stats. */
+char *
+geoip_format_entry_stats(time_t now)
+{
+  char t[ISO_TIME_LEN+1];
+  char *data = NULL;
+  char *result;
+
+  if (!start_of_entry_stats_interval)
+    return NULL; /* Not initialized. */
+
+  data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
+  format_iso_time(t, now);
+  tor_asprintf(&result, "entry-stats-end %s (%u s)\nentry-ips %s\n",
+              t, (unsigned) (now - start_of_entry_stats_interval),
+              data ? data : "");
+  tor_free(data);
+  return result;
+}
+
+/** If 24 hours have passed since the beginning of the current entry stats
+ * period, write entry stats to $DATADIR/stats/entry-stats (possibly
+ * overwriting an existing file) and reset counters.  Return when we would
+ * next want to write entry stats or 0 if we never want to write. */
 time_t
 time_t
 geoip_entry_stats_write(time_t now)
 geoip_entry_stats_write(time_t now)
 {
 {
-  char *statsdir = NULL, *filename = NULL;
-  char *data = NULL;
-  char written[ISO_TIME_LEN+1];
-  open_file_t *open_file = NULL;
-  FILE *out;
+  char *statsdir = NULL, *filename = NULL, *str = NULL;
 
 
   if (!start_of_entry_stats_interval)
   if (!start_of_entry_stats_interval)
     return 0; /* Not initialized. */
     return 0; /* Not initialized. */
@@ -1323,31 +1388,26 @@ geoip_entry_stats_write(time_t now)
   /* Discard all items in the client history that are too old. */
   /* Discard all items in the client history that are too old. */
   geoip_remove_old_clients(start_of_entry_stats_interval);
   geoip_remove_old_clients(start_of_entry_stats_interval);
 
 
+  /* Generate history string .*/
+  str = geoip_format_entry_stats(now);
+
+  /* Write entry-stats string to disk. */
   statsdir = get_datadir_fname("stats");
   statsdir = get_datadir_fname("stats");
-  if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0)
+  if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
+    log_warn(LD_HIST, "Unable to create stats/ directory!");
     goto done;
     goto done;
+  }
   filename = get_datadir_fname2("stats", "entry-stats");
   filename = get_datadir_fname2("stats", "entry-stats");
-  data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
-  format_iso_time(written, now);
-  out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND | O_TEXT,
-                                    0600, &open_file);
-  if (!out)
-    goto done;
-  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;
+  if (write_str_to_file(filename, str, 0) < 0)
+    log_warn(LD_HIST, "Unable to write entry statistics to disk!");
 
 
-  start_of_entry_stats_interval = now;
+  /* Reset measurement interval start. */
+  geoip_reset_entry_stats(now);
 
 
-  finish_writing_to_file(open_file);
-  open_file = NULL;
  done:
  done:
-  if (open_file)
-    abort_writing_to_file(open_file);
-  tor_free(filename);
   tor_free(statsdir);
   tor_free(statsdir);
-  tor_free(data);
+  tor_free(filename);
+  tor_free(str);
   return start_of_entry_stats_interval + WRITE_STATS_INTERVAL;
   return start_of_entry_stats_interval + WRITE_STATS_INTERVAL;
 }
 }
 
 

+ 5 - 0
src/or/geoip.h

@@ -43,12 +43,17 @@ void geoip_change_dirreq_state(uint64_t dirreq_id, dirreq_type_t type,
                                dirreq_state_t new_state);
                                dirreq_state_t new_state);
 
 
 void geoip_dirreq_stats_init(time_t now);
 void geoip_dirreq_stats_init(time_t now);
+void geoip_reset_dirreq_stats(time_t now);
+char *geoip_format_dirreq_stats(time_t now);
 time_t geoip_dirreq_stats_write(time_t now);
 time_t geoip_dirreq_stats_write(time_t now);
 void geoip_dirreq_stats_term(void);
 void geoip_dirreq_stats_term(void);
 void geoip_entry_stats_init(time_t now);
 void geoip_entry_stats_init(time_t now);
 time_t geoip_entry_stats_write(time_t now);
 time_t geoip_entry_stats_write(time_t now);
 void geoip_entry_stats_term(void);
 void geoip_entry_stats_term(void);
+void geoip_reset_entry_stats(time_t now);
+char *geoip_format_entry_stats(time_t now);
 void geoip_bridge_stats_init(time_t now);
 void geoip_bridge_stats_init(time_t now);
+char *geoip_format_bridge_stats(time_t now);
 time_t geoip_bridge_stats_write(time_t now);
 time_t geoip_bridge_stats_write(time_t now);
 void geoip_bridge_stats_term(void);
 void geoip_bridge_stats_term(void);
 const char *geoip_get_bridge_stats_extrainfo(time_t);
 const char *geoip_get_bridge_stats_extrainfo(time_t);

+ 19 - 1
src/or/main.c

@@ -114,6 +114,8 @@ static time_t time_to_check_for_correct_dns = 0;
 static time_t time_of_last_signewnym = 0;
 static time_t time_of_last_signewnym = 0;
 /** Is there a signewnym request we're currently waiting to handle? */
 /** Is there a signewnym request we're currently waiting to handle? */
 static int signewnym_is_pending = 0;
 static int signewnym_is_pending = 0;
+/** How many times have we called newnym? */
+static unsigned newnym_epoch = 0;
 
 
 /** Smartlist of all open connections. */
 /** Smartlist of all open connections. */
 static smartlist_t *connection_array = NULL;
 static smartlist_t *connection_array = NULL;
@@ -152,6 +154,12 @@ int can_complete_circuit=0;
  * they are obsolete? */
  * they are obsolete? */
 #define TLS_HANDSHAKE_TIMEOUT (60)
 #define TLS_HANDSHAKE_TIMEOUT (60)
 
 
+/** Decides our behavior when no logs are configured/before any
+ * logs have been configured.  For 0, we log notice to stdout as normal.
+ * For 1, we log warnings only.  For 2, we log nothing.
+ */
+int quiet_level = 0;
+
 /********* END VARIABLES ************/
 /********* END VARIABLES ************/
 
 
 /****************************************************************************
 /****************************************************************************
@@ -290,7 +298,7 @@ connection_unregister_events(connection_t *conn)
     conn->bufev = NULL;
     conn->bufev = NULL;
   }
   }
 #endif
 #endif
-  if (conn->dns_server_port) {
+  if (conn->type == CONN_TYPE_AP_DNS_LISTENER) {
     dnsserv_close_listener(conn);
     dnsserv_close_listener(conn);
   }
   }
 }
 }
@@ -1038,9 +1046,18 @@ signewnym_impl(time_t now)
   time_of_last_signewnym = now;
   time_of_last_signewnym = now;
   signewnym_is_pending = 0;
   signewnym_is_pending = 0;
 
 
+  ++newnym_epoch;
+
   control_event_signal(SIGNEWNYM);
   control_event_signal(SIGNEWNYM);
 }
 }
 
 
+/** Return the number of times that signewnym has been called. */
+unsigned
+get_signewnym_epoch(void)
+{
+  return newnym_epoch;
+}
+
 /** Perform regular maintenance tasks.  This function gets run once per
 /** Perform regular maintenance tasks.  This function gets run once per
  * second by second_elapsed_callback().
  * second by second_elapsed_callback().
  */
  */
@@ -2137,6 +2154,7 @@ tor_init(int argc, char *argv[])
     default:
     default:
       add_temp_log(LOG_NOTICE);
       add_temp_log(LOG_NOTICE);
   }
   }
+  quiet_level = quiet;
 
 
   log(LOG_NOTICE, LD_GENERAL, "Tor v%s. This is experimental software. "
   log(LOG_NOTICE, LD_GENERAL, "Tor v%s. This is experimental software. "
       "Do not rely on it for strong anonymity. (Running on %s)",get_version(),
       "Do not rely on it for strong anonymity. (Running on %s)",get_version(),

+ 1 - 0
src/or/main.h

@@ -52,6 +52,7 @@ void ip_address_changed(int at_interface);
 void dns_servers_relaunch_checks(void);
 void dns_servers_relaunch_checks(void);
 
 
 long get_uptime(void);
 long get_uptime(void);
+unsigned get_signewnym_epoch(void);
 
 
 void handle_signals(int is_parent);
 void handle_signals(int is_parent);
 void process_signal(uintptr_t sig);
 void process_signal(uintptr_t sig);

+ 0 - 6
src/or/microdesc.c

@@ -698,14 +698,8 @@ we_use_microdescriptors_for_circuits(const or_options_t *options)
   int ret = options->UseMicrodescriptors;
   int ret = options->UseMicrodescriptors;
   if (ret == -1) {
   if (ret == -1) {
     /* UseMicrodescriptors is "auto"; we need to decide: */
     /* UseMicrodescriptors is "auto"; we need to decide: */
-#if 0
     /* So we decide that we'll use microdescriptors iff we are not a server */
     /* So we decide that we'll use microdescriptors iff we are not a server */
     ret = ! server_mode(options);
     ret = ! server_mode(options);
-#else
-    /* We don't use microdescs for now: not enough caches are running
-     * 0.2.3.1-alpha */
-    ret = 0;
-#endif
   }
   }
   return ret;
   return ret;
 }
 }

+ 0 - 7
src/or/networkstatus.c

@@ -2006,13 +2006,6 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
                          tor_memcmp(rs->identity_digest,
                          tor_memcmp(rs->identity_digest,
                                router->cache_info.identity_digest, DIGEST_LEN),
                                router->cache_info.identity_digest, DIGEST_LEN),
   {
   {
-#if 0
-    /* We have no routerstatus for this router. Clear flags and skip it. */
-    if (!authdir) {
-      if (router->purpose == ROUTER_PURPOSE_GENERAL)
-        router_clear_status_flags(router);
-    }
-#endif
   }) {
   }) {
     /* We have a routerstatus for this router. */
     /* We have a routerstatus for this router. */
     const char *digest = router->cache_info.identity_digest;
     const char *digest = router->cache_info.identity_digest;

+ 188 - 11
src/or/or.h

@@ -930,6 +930,11 @@ typedef struct {
 
 
 typedef struct buf_t buf_t;
 typedef struct buf_t buf_t;
 typedef struct socks_request_t socks_request_t;
 typedef struct socks_request_t socks_request_t;
+#ifdef USE_BUFFEREVENTS
+#define generic_buffer_t struct evbuffer
+#else
+#define generic_buffer_t buf_t
+#endif
 
 
 /* Values for connection_t.magic: used to make sure that downcasts (casts from
 /* Values for connection_t.magic: used to make sure that downcasts (casts from
 * connection_t to foo_connection_t) are safe. */
 * connection_t to foo_connection_t) are safe. */
@@ -938,6 +943,7 @@ typedef struct socks_request_t socks_request_t;
 #define EDGE_CONNECTION_MAGIC 0xF0374013u
 #define EDGE_CONNECTION_MAGIC 0xF0374013u
 #define DIR_CONNECTION_MAGIC 0x9988ffeeu
 #define DIR_CONNECTION_MAGIC 0x9988ffeeu
 #define CONTROL_CONNECTION_MAGIC 0x8abc765du
 #define CONTROL_CONNECTION_MAGIC 0x8abc765du
+#define LISTENER_CONNECTION_MAGIC 0x1a1ac741u
 
 
 /** Description of a connection to another host or process, and associated
 /** Description of a connection to another host or process, and associated
  * data.
  * data.
@@ -1043,15 +1049,31 @@ typedef struct connection_t {
   /** Unique identifier for this connection on this Tor instance. */
   /** Unique identifier for this connection on this Tor instance. */
   uint64_t global_identifier;
   uint64_t global_identifier;
 
 
-  /* XXXX023 move this field, and all the listener-only fields (just
-     socket_family, I think), into a new listener_connection_t subtype. */
+  /** Unique ID for measuring tunneled network status requests. */
+  uint64_t dirreq_id;
+} connection_t;
+
+typedef struct listener_connection_t {
+  connection_t _base;
+
   /** If the connection is a CONN_TYPE_AP_DNS_LISTENER, this field points
   /** If the connection is a CONN_TYPE_AP_DNS_LISTENER, this field points
    * to the evdns_server_port it uses to listen to and answer connections. */
    * to the evdns_server_port it uses to listen to and answer connections. */
   struct evdns_server_port *dns_server_port;
   struct evdns_server_port *dns_server_port;
 
 
-  /** Unique ID for measuring tunneled network status requests. */
-  uint64_t dirreq_id;
-} connection_t;
+  /** @name Isolation parameters
+   *
+   * For an AP listener, these fields describe how to isolate streams that
+   * arrive on the listener.
+   *
+   * @{
+   */
+  /** The session group for this listener. */
+  int session_group;
+  /** One or more ISO_ flags to describe how to isolate streams. */
+  uint8_t isolation_flags;
+  /**@}*/
+
+} listener_connection_t;
 
 
 /** Stores flags and information related to the portion of a v2 Tor OR
 /** Stores flags and information related to the portion of a v2 Tor OR
  * connection handshake that happens after the TLS handshake is finished.
  * connection handshake that happens after the TLS handshake is finished.
@@ -1190,6 +1212,20 @@ typedef struct edge_connection_t {
   /** What rendezvous service are we querying for? (AP only) */
   /** What rendezvous service are we querying for? (AP only) */
   rend_data_t *rend_data;
   rend_data_t *rend_data;
 
 
+  /* === Isolation related, AP only. === */
+  /** AP only: based on which factors do we isolate this stream? */
+  uint8_t isolation_flags;
+  /** AP only: what session group is this stream in? */
+  int session_group;
+  /** AP only: The newnym epoch in which we created this connection. */
+  unsigned nym_epoch;
+  /** AP only: The original requested address before we rewrote it. */
+  char *original_dest_address;
+  /* Other fields to isolate on already exist.  The ClientAddr is addr.  The
+     ClientProtocol is a combination of type and socks_request->
+     socks_version.  SocksAuth is socks_request->username/password.
+     DestAddr is in socks_request->address. */
+
   /** Number of times we've reassigned this application connection to
   /** Number of times we've reassigned this application connection to
    * a new circuit. We keep track because the timeout is longer if we've
    * a new circuit. We keep track because the timeout is longer if we've
    * already retried several times. */
    * already retried several times. */
@@ -1232,6 +1268,21 @@ typedef struct edge_connection_t {
    * NATd connection */
    * NATd connection */
   unsigned int is_transparent_ap:1;
   unsigned int is_transparent_ap:1;
 
 
+  /** For AP connections only: Set if this connection's target exit node
+   * allows optimistic data (that is, data sent on this stream before
+   * the exit has sent a CONNECTED cell) and we have chosen to use it.
+   */
+  unsigned int may_use_optimistic_data : 1;
+
+  /** For AP connections only: buffer for data that we have sent
+   * optimistically, which we might need to re-send if we have to
+   * retry this connection. */
+  generic_buffer_t *pending_optimistic_data;
+  /* For AP connections only: buffer for data that we previously sent
+  * optimistically which we are currently re-sending as we retry this
+  * connection. */
+  generic_buffer_t *sending_optimistic_data;
+
   /** If this is a DNSPort connection, this field holds the pending DNS
   /** If this is a DNSPort connection, this field holds the pending DNS
    * request that we're going to try to answer.  */
    * request that we're going to try to answer.  */
   struct evdns_server_request *dns_server_request;
   struct evdns_server_request *dns_server_request;
@@ -1321,6 +1372,9 @@ static edge_connection_t *TO_EDGE_CONN(connection_t *);
 /** Convert a connection_t* to an control_connection_t*; assert if the cast is
 /** Convert a connection_t* to an control_connection_t*; assert if the cast is
  * invalid. */
  * invalid. */
 static control_connection_t *TO_CONTROL_CONN(connection_t *);
 static control_connection_t *TO_CONTROL_CONN(connection_t *);
+/** Convert a connection_t* to an listener_connection_t*; assert if the cast is
+ * invalid. */
+static listener_connection_t *TO_LISTENER_CONN(connection_t *);
 
 
 static INLINE or_connection_t *TO_OR_CONN(connection_t *c)
 static INLINE or_connection_t *TO_OR_CONN(connection_t *c)
 {
 {
@@ -1342,6 +1396,11 @@ static INLINE control_connection_t *TO_CONTROL_CONN(connection_t *c)
   tor_assert(c->magic == CONTROL_CONNECTION_MAGIC);
   tor_assert(c->magic == CONTROL_CONNECTION_MAGIC);
   return DOWNCAST(control_connection_t, c);
   return DOWNCAST(control_connection_t, c);
 }
 }
+static INLINE listener_connection_t *TO_LISTENER_CONN(connection_t *c)
+{
+  tor_assert(c->magic == LISTENER_CONNECTION_MAGIC);
+  return DOWNCAST(listener_connection_t, c);
+}
 
 
 /* Conditional macros to help write code that works whether bufferevents are
 /* Conditional macros to help write code that works whether bufferevents are
    disabled or not.
    disabled or not.
@@ -1667,6 +1726,9 @@ typedef struct routerstatus_t {
   /** True iff this router is a version that, if it caches directory info,
   /** True iff this router is a version that, if it caches directory info,
    * we can get microdescriptors from. */
    * we can get microdescriptors from. */
   unsigned int version_supports_microdesc_cache:1;
   unsigned int version_supports_microdesc_cache:1;
+  /** True iff this router is a version that allows DATA cells to arrive on
+   * a stream before it has sent a CONNECTED cell. */
+  unsigned int version_supports_optimistic_data:1;
 
 
   unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */
   unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */
   unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */
   unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */
@@ -2424,6 +2486,55 @@ typedef struct origin_circuit_t {
   /* XXXX NM This can get re-used after 2**32 circuits. */
   /* XXXX NM This can get re-used after 2**32 circuits. */
   uint32_t global_identifier;
   uint32_t global_identifier;
 
 
+  /** True if we have associated one stream to this circuit, thereby setting
+   * the isolation paramaters for this circuit.  Note that this doesn't
+   * necessarily mean that we've <em>attached</em> any streams to the circuit:
+   * we may only have marked up this circuit during the launch process.
+   */
+  unsigned int isolation_values_set : 1;
+  /** True iff any stream has <em>ever</em> been attached to this circuit.
+   *
+   * In a better world we could use timestamp_dirty for this, but
+   * timestamp_dirty is far too overloaded at the moment.
+   */
+  unsigned int isolation_any_streams_attached : 1;
+
+  /** A bitfield of ISO_* flags for every isolation field such that this
+   * circuit has had streams with more than one value for that field
+   * attached to it. */
+  uint8_t isolation_flags_mixed;
+
+  /** @name Isolation parameters
+   *
+   * If any streams have been associated with this circ (isolation_values_set
+   * == 1), and all streams associated with the circuit have had the same
+   * value for some field ((isolation_flags_mixed & ISO_FOO) == 0), then these
+   * elements hold the value for that field.
+   *
+   * Note again that "associated" is not the same as "attached": we
+   * preliminarily associate streams with a circuit while the circuit is being
+   * launched, so that we can tell whether we need to launch more circuits.
+   *
+   * @{
+   */
+  uint8_t client_proto_type;
+  uint8_t client_proto_socksver;
+  uint16_t dest_port;
+  tor_addr_t client_addr;
+  char *dest_address;
+  int session_group;
+  unsigned nym_epoch;
+  size_t socks_username_len;
+  uint8_t socks_password_len;
+  /* Note that the next two values are NOT NUL-terminated; see
+     socks_username_len and socks_password_len for their lengths. */
+  char *socks_username;
+  char *socks_password;
+  /** Global identifier for the first stream attached here; used by
+   * ISO_STREAM. */
+  uint64_t associated_isolated_stream_global_id;
+  /**@}*/
+
 } origin_circuit_t;
 } origin_circuit_t;
 
 
 /** An or_circuit_t holds information needed to implement a circuit at an
 /** An or_circuit_t holds information needed to implement a circuit at an
@@ -2542,6 +2653,60 @@ typedef enum invalid_router_usage_t {
 #define MIN_CONSTRAINED_TCP_BUFFER 2048
 #define MIN_CONSTRAINED_TCP_BUFFER 2048
 #define MAX_CONSTRAINED_TCP_BUFFER 262144  /* 256k */
 #define MAX_CONSTRAINED_TCP_BUFFER 262144  /* 256k */
 
 
+/** @name Isolation flags
+
+    Ways to isolate client streams
+
+    @{
+*/
+/** Isolate based on destination port */
+#define ISO_DESTPORT    (1u<<0)
+/** Isolate based on destination address */
+#define ISO_DESTADDR    (1u<<1)
+/** Isolate based on SOCKS authentication */
+#define ISO_SOCKSAUTH   (1u<<2)
+/** Isolate based on client protocol choice */
+#define ISO_CLIENTPROTO (1u<<3)
+/** Isolate based on client address */
+#define ISO_CLIENTADDR  (1u<<4)
+/** Isolate based on session group (always on). */
+#define ISO_SESSIONGRP  (1u<<5)
+/** Isolate based on newnym epoch (always on). */
+#define ISO_NYM_EPOCH   (1u<<6)
+/** Isolate all streams (Internal only). */
+#define ISO_STREAM      (1u<<7)
+/**@}*/
+
+/** Default isolation level for ports. */
+#define ISO_DEFAULT (ISO_CLIENTADDR|ISO_SOCKSAUTH|ISO_SESSIONGRP|ISO_NYM_EPOCH)
+
+/** Indicates that we haven't yet set a session group on a port_cfg_t. */
+#define SESSION_GROUP_UNSET -1
+/** Session group reserved for directory connections */
+#define SESSION_GROUP_DIRCONN -2
+/** Session group reserved for resolve requests launched by a controller */
+#define SESSION_GROUP_CONTROL_RESOLVE -3
+/** First automatically allocated session group number */
+#define SESSION_GROUP_FIRST_AUTO -4
+
+/** Configuration for a single port that we're listening on. */
+typedef struct port_cfg_t {
+  tor_addr_t addr; /**< The actual IP to listen on, if !is_unix_addr. */
+  int port; /**< The configured port, or CFG_AUTO_PORT to tell Tor to pick its
+             * own port. */
+  uint8_t type; /**< One of CONN_TYPE_*_LISTENER */
+  unsigned is_unix_addr : 1; /**< True iff this is an AF_UNIX address. */
+
+  /* Client port types (socks, dns, trans, natd) only: */
+  uint8_t isolation_flags; /**< Zero or more isolation flags */
+  int session_group; /**< A session group, or -1 if this port is not in a
+                      * session group. */
+
+  /* Unix sockets only: */
+  /** Path for an AF_UNIX address */
+  char unix_addr[FLEXIBLE_ARRAY_MEMBER];
+} port_cfg_t;
+
 /** A linked list of lines in a config file. */
 /** A linked list of lines in a config file. */
 typedef struct config_line_t {
 typedef struct config_line_t {
   char *key;
   char *key;
@@ -2637,16 +2802,17 @@ typedef struct {
   char *User; /**< Name of user to run Tor as. */
   char *User; /**< Name of user to run Tor as. */
   char *Group; /**< Name of group to run Tor as. */
   char *Group; /**< Name of group to run Tor as. */
   int ORPort; /**< Port to listen on for OR connections. */
   int ORPort; /**< Port to listen on for OR connections. */
-  int SocksPort; /**< Port to listen on for SOCKS connections. */
-  /** Port to listen on for transparent pf/netfilter connections. */
-  int TransPort;
-  int NATDPort; /**< Port to listen on for transparent natd connections. */
+  config_line_t *SocksPort; /**< Ports to listen on for SOCKS connections. */
+  /** Ports to listen on for transparent pf/netfilter connections. */
+  config_line_t *TransPort;
+  config_line_t *NATDPort; /**< Ports to listen on for transparent natd
+                            * connections. */
   int ControlPort; /**< Port to listen on for control connections. */
   int ControlPort; /**< Port to listen on for control connections. */
   config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on
   config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on
                                  * for control connections. */
                                  * for control connections. */
   int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */
   int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */
   int DirPort; /**< Port to listen on for directory connections. */
   int DirPort; /**< Port to listen on for directory connections. */
-  int DNSPort; /**< Port to listen on for DNS requests. */
+  config_line_t *DNSPort; /**< Port to listen on for DNS requests. */
   int AssumeReachable; /**< Whether to publish our descriptor regardless. */
   int AssumeReachable; /**< Whether to publish our descriptor regardless. */
   int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */
   int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */
   int V1AuthoritativeDir; /**< Boolean: is this an authoritative directory
   int V1AuthoritativeDir; /**< Boolean: is this an authoritative directory
@@ -3108,6 +3274,15 @@ typedef struct {
   /** Should that file be group-readable? */
   /** Should that file be group-readable? */
   int ControlPortFileGroupReadable;
   int ControlPortFileGroupReadable;
 
 
+#define MAX_MAX_CLIENT_CIRCUITS_PENDING 1024
+  /** Maximum number of non-open general-purpose origin circuits to allow at
+   * once. */
+  int MaxClientCircuitsPending;
+
+  /** If 1, we always send optimistic data when it's supported.  If 0, we
+   * never use it.  If -1, we do what the consensus says. */
+  int OptimisticData;
+
 } or_options_t;
 } or_options_t;
 
 
 /** Persistent state for an onion router, as saved to disk. */
 /** Persistent state for an onion router, as saved to disk. */
@@ -3214,6 +3389,8 @@ struct socks_request_t {
   uint8_t auth_type;
   uint8_t auth_type;
   /** What is this stream's goal? One of the SOCKS_COMMAND_* values */
   /** What is this stream's goal? One of the SOCKS_COMMAND_* values */
   uint8_t command;
   uint8_t command;
+  /** Which kind of listener created this stream? */
+  uint8_t listener_type;
   size_t replylen; /**< Length of <b>reply</b>. */
   size_t replylen; /**< Length of <b>reply</b>. */
   uint8_t reply[MAX_SOCKS_REPLY_LEN]; /**< Write an entry into this string if
   uint8_t reply[MAX_SOCKS_REPLY_LEN]; /**< Write an entry into this string if
                                     * we want to specify our own socks reply,
                                     * we want to specify our own socks reply,
@@ -3230,7 +3407,7 @@ struct socks_request_t {
   unsigned int got_auth : 1; /**< Have we received any authentication data? */
   unsigned int got_auth : 1; /**< Have we received any authentication data? */
 
 
   /** Number of bytes in username; 0 if username is NULL */
   /** Number of bytes in username; 0 if username is NULL */
-  uint8_t usernamelen;
+  size_t usernamelen;
   /** Number of bytes in password; 0 if password is NULL */
   /** Number of bytes in password; 0 if password is NULL */
   uint8_t passwordlen;
   uint8_t passwordlen;
   /** The negotiated username value if any (for socks5), or the entire
   /** The negotiated username value if any (for socks5), or the entire

+ 42 - 2
src/or/relay.c

@@ -944,6 +944,12 @@ connection_edge_process_relay_cell_not_open(
           break;
           break;
       }
       }
     }
     }
+    /* This is definitely a success, so forget about any pending data we
+     * had sent. */
+    if (conn->pending_optimistic_data) {
+      generic_buffer_free(conn->pending_optimistic_data);
+      conn->pending_optimistic_data = NULL;
+    }
 
 
     /* handle anything that might have queued */
     /* handle anything that might have queued */
     if (connection_edge_package_raw_inbuf(conn, 1, NULL) < 0) {
     if (connection_edge_package_raw_inbuf(conn, 1, NULL) < 0) {
@@ -1343,6 +1349,10 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
   char payload[CELL_PAYLOAD_SIZE];
   char payload[CELL_PAYLOAD_SIZE];
   circuit_t *circ;
   circuit_t *circ;
   unsigned domain = conn->cpath_layer ? LD_APP : LD_EXIT;
   unsigned domain = conn->cpath_layer ? LD_APP : LD_EXIT;
+  int sending_from_optimistic = 0;
+  const int sending_optimistically =
+    conn->_base.type == CONN_TYPE_AP &&
+    conn->_base.state != AP_CONN_STATE_OPEN;
 
 
   tor_assert(conn);
   tor_assert(conn);
 
 
@@ -1375,7 +1385,18 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
     return 0;
     return 0;
   }
   }
 
 
-  amount_to_process = connection_get_inbuf_len(TO_CONN(conn));
+  sending_from_optimistic = conn->sending_optimistic_data != NULL;
+
+  if (PREDICT_UNLIKELY(sending_from_optimistic)) {
+    amount_to_process = generic_buffer_len(conn->sending_optimistic_data);
+    if (PREDICT_UNLIKELY(!amount_to_process)) {
+      log_warn(LD_BUG, "sending_optimistic_data was non-NULL but empty");
+      amount_to_process = connection_get_inbuf_len(TO_CONN(conn));
+      sending_from_optimistic = 0;
+    }
+  } else {
+    amount_to_process = connection_get_inbuf_len(TO_CONN(conn));
+  }
 
 
   if (!amount_to_process)
   if (!amount_to_process)
     return 0;
     return 0;
@@ -1391,11 +1412,30 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
   stats_n_data_bytes_packaged += length;
   stats_n_data_bytes_packaged += length;
   stats_n_data_cells_packaged += 1;
   stats_n_data_cells_packaged += 1;
 
 
-  connection_fetch_from_buf(payload, length, TO_CONN(conn));
+  if (PREDICT_UNLIKELY(sending_from_optimistic)) {
+    /* XXX023 We could be more efficient here by sometimes packing
+     * previously-sent optimistic data in the same cell with data
+     * from the inbuf. */
+    generic_buffer_get(conn->sending_optimistic_data, payload, length);
+    if (!generic_buffer_len(conn->sending_optimistic_data)) {
+        generic_buffer_free(conn->sending_optimistic_data);
+        conn->sending_optimistic_data = NULL;
+    }
+  } else {
+    connection_fetch_from_buf(payload, length, TO_CONN(conn));
+  }
 
 
   log_debug(domain,"(%d) Packaging %d bytes (%d waiting).", conn->_base.s,
   log_debug(domain,"(%d) Packaging %d bytes (%d waiting).", conn->_base.s,
             (int)length, (int)connection_get_inbuf_len(TO_CONN(conn)));
             (int)length, (int)connection_get_inbuf_len(TO_CONN(conn)));
 
 
+  if (sending_optimistically && !sending_from_optimistic) {
+    /* This is new optimistic data; remember it in case we need to detach and
+       retry */
+    if (!conn->pending_optimistic_data)
+      conn->pending_optimistic_data = generic_buffer_new();
+    generic_buffer_add(conn->pending_optimistic_data, payload, length);
+  }
+
   if (connection_edge_send_command(conn, RELAY_COMMAND_DATA,
   if (connection_edge_send_command(conn, RELAY_COMMAND_DATA,
                                    payload, length) < 0 )
                                    payload, length) < 0 )
     /* circuit got marked for close, don't continue, don't need to mark conn */
     /* circuit got marked for close, don't continue, don't need to mark conn */

+ 142 - 98
src/or/rephist.c

@@ -2364,23 +2364,41 @@ typedef struct circ_buffer_stats_t {
 /** List of circ_buffer_stats_t. */
 /** List of circ_buffer_stats_t. */
 static smartlist_t *circuits_for_buffer_stats = NULL;
 static smartlist_t *circuits_for_buffer_stats = NULL;
 
 
+/** Remember cell statistics <b>mean_num_cells_in_queue</b>,
+ * <b>mean_time_cells_in_queue</b>, and <b>processed_cells</b> of a
+ * circuit. */
+void
+rep_hist_add_buffer_stats(double mean_num_cells_in_queue,
+    double mean_time_cells_in_queue, uint32_t processed_cells)
+{
+  circ_buffer_stats_t *stat;
+  if (!start_of_buffer_stats_interval)
+    return; /* Not initialized. */
+  stat = tor_malloc_zero(sizeof(circ_buffer_stats_t));
+  stat->mean_num_cells_in_queue = mean_num_cells_in_queue;
+  stat->mean_time_cells_in_queue = mean_time_cells_in_queue;
+  stat->processed_cells = processed_cells;
+  if (!circuits_for_buffer_stats)
+    circuits_for_buffer_stats = smartlist_create();
+  smartlist_add(circuits_for_buffer_stats, stat);
+}
+
 /** Remember cell statistics for circuit <b>circ</b> at time
 /** Remember cell statistics for circuit <b>circ</b> at time
  * <b>end_of_interval</b> and reset cell counters in case the circuit
  * <b>end_of_interval</b> and reset cell counters in case the circuit
  * remains open in the next measurement interval. */
  * remains open in the next measurement interval. */
 void
 void
 rep_hist_buffer_stats_add_circ(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;
   time_t start_of_interval;
   int interval_length;
   int interval_length;
   or_circuit_t *orcirc;
   or_circuit_t *orcirc;
+  double mean_num_cells_in_queue, mean_time_cells_in_queue;
+  uint32_t processed_cells;
   if (CIRCUIT_IS_ORIGIN(circ))
   if (CIRCUIT_IS_ORIGIN(circ))
     return;
     return;
   orcirc = TO_OR_CIRCUIT(circ);
   orcirc = TO_OR_CIRCUIT(circ);
   if (!orcirc->processed_cells)
   if (!orcirc->processed_cells)
     return;
     return;
-  if (!circuits_for_buffer_stats)
-    circuits_for_buffer_stats = smartlist_create();
   start_of_interval = (circ->timestamp_created.tv_sec >
   start_of_interval = (circ->timestamp_created.tv_sec >
                        start_of_buffer_stats_interval) ?
                        start_of_buffer_stats_interval) ?
         circ->timestamp_created.tv_sec :
         circ->timestamp_created.tv_sec :
@@ -2388,17 +2406,18 @@ rep_hist_buffer_stats_add_circ(circuit_t *circ, time_t end_of_interval)
   interval_length = (int) (end_of_interval - start_of_interval);
   interval_length = (int) (end_of_interval - start_of_interval);
   if (interval_length <= 0)
   if (interval_length <= 0)
     return;
     return;
-  stat = tor_malloc_zero(sizeof(circ_buffer_stats_t));
-  stat->processed_cells = orcirc->processed_cells;
+  processed_cells = orcirc->processed_cells;
   /* 1000.0 for s -> ms; 2.0 because of app-ward and exit-ward queues */
   /* 1000.0 for s -> ms; 2.0 because of app-ward and exit-ward queues */
-  stat->mean_num_cells_in_queue = (double) orcirc->total_cell_waiting_time /
+  mean_num_cells_in_queue = (double) orcirc->total_cell_waiting_time /
       (double) interval_length / 1000.0 / 2.0;
       (double) interval_length / 1000.0 / 2.0;
-  stat->mean_time_cells_in_queue =
+  mean_time_cells_in_queue =
       (double) orcirc->total_cell_waiting_time /
       (double) orcirc->total_cell_waiting_time /
       (double) orcirc->processed_cells;
       (double) orcirc->processed_cells;
-  smartlist_add(circuits_for_buffer_stats, stat);
   orcirc->total_cell_waiting_time = 0;
   orcirc->total_cell_waiting_time = 0;
   orcirc->processed_cells = 0;
   orcirc->processed_cells = 0;
+  rep_hist_add_buffer_stats(mean_num_cells_in_queue,
+                            mean_time_cells_in_queue,
+                            processed_cells);
 }
 }
 
 
 /** Sorting helper: return -1, 1, or 0 based on comparison of two
 /** Sorting helper: return -1, 1, or 0 based on comparison of two
@@ -2420,135 +2439,160 @@ _buffer_stats_compare_entries(const void **_a, const void **_b)
 void
 void
 rep_hist_buffer_stats_term(void)
 rep_hist_buffer_stats_term(void)
 {
 {
-  start_of_buffer_stats_interval = 0;
+  rep_hist_reset_buffer_stats(0);
+}
+
+/** Clear history of circuit statistics and set the measurement interval
+ * start to <b>now</b>. */
+void
+rep_hist_reset_buffer_stats(time_t now)
+{
   if (!circuits_for_buffer_stats)
   if (!circuits_for_buffer_stats)
     circuits_for_buffer_stats = smartlist_create();
     circuits_for_buffer_stats = smartlist_create();
   SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *,
   SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *,
       stat, tor_free(stat));
       stat, tor_free(stat));
   smartlist_clear(circuits_for_buffer_stats);
   smartlist_clear(circuits_for_buffer_stats);
+  start_of_buffer_stats_interval = now;
 }
 }
 
 
-/** Write buffer statistics to $DATADIR/stats/buffer-stats and return when
- * we would next want to write exit stats. */
-time_t
-rep_hist_buffer_stats_write(time_t now)
+/** Return a newly allocated string containing the buffer statistics until
+ * <b>now</b>, or NULL if we're not collecting buffer stats. */
+char *
+rep_hist_format_buffer_stats(time_t now)
 {
 {
-  char *statsdir = NULL, *filename = NULL;
-  char written[ISO_TIME_LEN+1];
-  open_file_t *open_file = NULL;
-  FILE *out;
 #define SHARES 10
 #define SHARES 10
   int processed_cells[SHARES], circs_in_share[SHARES],
   int processed_cells[SHARES], circs_in_share[SHARES],
       number_of_circuits, i;
       number_of_circuits, i;
   double queued_cells[SHARES], time_in_queue[SHARES];
   double queued_cells[SHARES], time_in_queue[SHARES];
-  smartlist_t *str_build = NULL;
-  char *str = NULL, *buf = NULL;
-  circuit_t *circ;
+  char *buf = NULL;
+  smartlist_t *processed_cells_strings, *queued_cells_strings,
+              *time_in_queue_strings;
+  char *processed_cells_string, *queued_cells_string,
+       *time_in_queue_string;
+  char t[ISO_TIME_LEN+1];
+  char *result;
 
 
   if (!start_of_buffer_stats_interval)
   if (!start_of_buffer_stats_interval)
-    return 0; /* Not initialized. */
-  if (start_of_buffer_stats_interval + WRITE_STATS_INTERVAL > now)
-    goto done; /* Not ready to write */
-
-  str_build = smartlist_create();
+    return NULL; /* Not initialized. */
 
 
-  /* add current circuits to stats */
-  for (circ = _circuit_get_global_list(); circ; circ = circ->next)
-    rep_hist_buffer_stats_add_circ(circ, now);
-  /* calculate deciles */
+  /* Calculate deciles if we saw at least one circuit. */
   memset(processed_cells, 0, SHARES * sizeof(int));
   memset(processed_cells, 0, SHARES * sizeof(int));
   memset(circs_in_share, 0, SHARES * sizeof(int));
   memset(circs_in_share, 0, SHARES * sizeof(int));
   memset(queued_cells, 0, SHARES * sizeof(double));
   memset(queued_cells, 0, SHARES * sizeof(double));
   memset(time_in_queue, 0, SHARES * sizeof(double));
   memset(time_in_queue, 0, SHARES * sizeof(double));
   if (!circuits_for_buffer_stats)
   if (!circuits_for_buffer_stats)
     circuits_for_buffer_stats = smartlist_create();
     circuits_for_buffer_stats = smartlist_create();
-  smartlist_sort(circuits_for_buffer_stats,
-                 _buffer_stats_compare_entries);
   number_of_circuits = smartlist_len(circuits_for_buffer_stats);
   number_of_circuits = smartlist_len(circuits_for_buffer_stats);
-  if (number_of_circuits < 1) {
-    log_info(LD_HIST, "Attempt to write cell statistics to disk failed. "
-             "We haven't seen a single circuit to report about.");
-    goto done;
+  if (number_of_circuits > 0) {
+    smartlist_sort(circuits_for_buffer_stats,
+                   _buffer_stats_compare_entries);
+    i = 0;
+    SMARTLIST_FOREACH_BEGIN(circuits_for_buffer_stats,
+                            circ_buffer_stats_t *, stat)
+    {
+      int share = i++ * SHARES / number_of_circuits;
+      processed_cells[share] += stat->processed_cells;
+      queued_cells[share] += stat->mean_num_cells_in_queue;
+      time_in_queue[share] += stat->mean_time_cells_in_queue;
+      circs_in_share[share]++;
+    }
+    SMARTLIST_FOREACH_END(stat);
   }
   }
-  i = 0;
-  SMARTLIST_FOREACH_BEGIN(circuits_for_buffer_stats,
-                          circ_buffer_stats_t *, stat)
-  {
-    int share = i++ * SHARES / number_of_circuits;
-    processed_cells[share] += stat->processed_cells;
-    queued_cells[share] += stat->mean_num_cells_in_queue;
-    time_in_queue[share] += stat->mean_time_cells_in_queue;
-    circs_in_share[share]++;
-  }
-  SMARTLIST_FOREACH_END(stat);
-  /* clear buffer stats history */
-  SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *,
-      stat, tor_free(stat));
-  smartlist_clear(circuits_for_buffer_stats);
-  /* write to file */
-  statsdir = get_datadir_fname("stats");
-  if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0)
-    goto done;
-  filename = get_datadir_fname2("stats", "buffer-stats");
-  out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND | O_TEXT,
-                                    0600, &open_file);
-  if (!out)
-    goto done;
-  format_iso_time(written, now);
-  if (fprintf(out, "cell-stats-end %s (%d s)\n", written,
-              (unsigned) (now - start_of_buffer_stats_interval)) < 0)
-    goto done;
+
+  /* Write deciles to strings. */
+  processed_cells_strings = smartlist_create();
+  queued_cells_strings = smartlist_create();
+  time_in_queue_strings = smartlist_create();
   for (i = 0; i < SHARES; i++) {
   for (i = 0; i < SHARES; i++) {
     tor_asprintf(&buf,"%d", !circs_in_share[i] ? 0 :
     tor_asprintf(&buf,"%d", !circs_in_share[i] ? 0 :
                  processed_cells[i] / circs_in_share[i]);
                  processed_cells[i] / circs_in_share[i]);
-    smartlist_add(str_build, buf);
+    smartlist_add(processed_cells_strings, buf);
   }
   }
-  str = smartlist_join_strings(str_build, ",", 0, NULL);
-  if (fprintf(out, "cell-processed-cells %s\n", str) < 0)
-    goto done;
-  tor_free(str);
-  SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
-  smartlist_clear(str_build);
   for (i = 0; i < SHARES; i++) {
   for (i = 0; i < SHARES; i++) {
     tor_asprintf(&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]);
                  queued_cells[i] / (double) circs_in_share[i]);
-    smartlist_add(str_build, buf);
+    smartlist_add(queued_cells_strings, buf);
   }
   }
-  str = smartlist_join_strings(str_build, ",", 0, NULL);
-  if (fprintf(out, "cell-queued-cells %s\n", str) < 0)
-    goto done;
-  tor_free(str);
-  SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
-  smartlist_clear(str_build);
   for (i = 0; i < SHARES; i++) {
   for (i = 0; i < SHARES; i++) {
     tor_asprintf(&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]);
                  time_in_queue[i] / (double) circs_in_share[i]);
-    smartlist_add(str_build, buf);
+    smartlist_add(time_in_queue_strings, buf);
   }
   }
-  str = smartlist_join_strings(str_build, ",", 0, NULL);
-  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, "cell-circuits-per-decile %d\n",
-              (number_of_circuits + SHARES - 1) / SHARES) < 0)
+
+  /* Join all observations in single strings. */
+  processed_cells_string = smartlist_join_strings(processed_cells_strings,
+                                                  ",", 0, NULL);
+  queued_cells_string = smartlist_join_strings(queued_cells_strings,
+                                               ",", 0, NULL);
+  time_in_queue_string = smartlist_join_strings(time_in_queue_strings,
+                                                ",", 0, NULL);
+  SMARTLIST_FOREACH(processed_cells_strings, char *, cp, tor_free(cp));
+  SMARTLIST_FOREACH(queued_cells_strings, char *, cp, tor_free(cp));
+  SMARTLIST_FOREACH(time_in_queue_strings, char *, cp, tor_free(cp));
+  smartlist_free(processed_cells_strings);
+  smartlist_free(queued_cells_strings);
+  smartlist_free(time_in_queue_strings);
+
+  /* Put everything together. */
+  format_iso_time(t, now);
+  tor_asprintf(&result, "cell-stats-end %s (%d s)\n"
+               "cell-processed-cells %s\n"
+               "cell-queued-cells %s\n"
+               "cell-time-in-queue %s\n"
+               "cell-circuits-per-decile %d\n",
+               t, (unsigned) (now - start_of_buffer_stats_interval),
+               processed_cells_string,
+               queued_cells_string,
+               time_in_queue_string,
+               (number_of_circuits + SHARES - 1) / SHARES);
+  tor_free(processed_cells_string);
+  tor_free(queued_cells_string);
+  tor_free(time_in_queue_string);
+  return result;
+#undef SHARES
+}
+
+/** If 24 hours have passed since the beginning of the current buffer
+ * stats period, write buffer stats to $DATADIR/stats/buffer-stats
+ * (possibly overwriting an existing file) and reset counters.  Return
+ * when we would next want to write buffer stats or 0 if we never want to
+ * write. */
+time_t
+rep_hist_buffer_stats_write(time_t now)
+{
+  circuit_t *circ;
+  char *statsdir = NULL, *filename = NULL, *str = NULL;
+
+  if (!start_of_buffer_stats_interval)
+    return 0; /* Not initialized. */
+  if (start_of_buffer_stats_interval + WRITE_STATS_INTERVAL > now)
+    goto done; /* Not ready to write */
+
+  /* Add open circuits to the history. */
+  for (circ = _circuit_get_global_list(); circ; circ = circ->next) {
+    rep_hist_buffer_stats_add_circ(circ, now);
+  }
+
+  /* Generate history string. */
+  str = rep_hist_format_buffer_stats(now);
+
+  /* Reset both buffer history and counters of open circuits. */
+  rep_hist_reset_buffer_stats(now);
+
+  /* Try to write to disk. */
+  statsdir = get_datadir_fname("stats");
+  if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
+    log_warn(LD_HIST, "Unable to create stats/ directory!");
     goto done;
     goto done;
-  finish_writing_to_file(open_file);
-  open_file = NULL;
-  start_of_buffer_stats_interval = now;
+  }
+  filename = get_datadir_fname2("stats", "buffer-stats");
+  if (write_str_to_file(filename, str, 0) < 0)
+    log_warn(LD_HIST, "Unable to write buffer stats to disk!");
+
  done:
  done:
-  if (open_file)
-    abort_writing_to_file(open_file);
+  tor_free(str);
   tor_free(filename);
   tor_free(filename);
   tor_free(statsdir);
   tor_free(statsdir);
-  if (str_build) {
-    SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
-    smartlist_free(str_build);
-  }
-  tor_free(str);
-#undef SHARES
   return start_of_buffer_stats_interval + WRITE_STATS_INTERVAL;
   return start_of_buffer_stats_interval + WRITE_STATS_INTERVAL;
 }
 }
 
 

+ 4 - 0
src/or/rephist.h

@@ -77,6 +77,10 @@ void rep_hist_buffer_stats_add_circ(circuit_t *circ,
                                     time_t end_of_interval);
                                     time_t end_of_interval);
 time_t rep_hist_buffer_stats_write(time_t now);
 time_t rep_hist_buffer_stats_write(time_t now);
 void rep_hist_buffer_stats_term(void);
 void rep_hist_buffer_stats_term(void);
+void rep_hist_add_buffer_stats(double mean_num_cells_in_queue,
+     double mean_time_cells_in_queue, uint32_t processed_cells);
+char *rep_hist_format_buffer_stats(time_t now);
+void rep_hist_reset_buffer_stats(time_t now);
 
 
 void rep_hist_conn_stats_init(time_t now);
 void rep_hist_conn_stats_init(time_t now);
 void rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
 void rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read,

+ 3 - 5
src/or/router.c

@@ -1116,14 +1116,12 @@ set_server_advertised(int s)
   server_is_advertised = s;
   server_is_advertised = s;
 }
 }
 
 
-/** Return true iff we are trying to be a socks proxy. */
+/** Return true iff we are trying to proxy client connections. */
 int
 int
 proxy_mode(const or_options_t *options)
 proxy_mode(const or_options_t *options)
 {
 {
-  return (options->SocksPort != 0 ||
-          options->TransPort != 0 ||
-          options->NATDPort != 0 ||
-          options->DNSPort != 0);
+  (void)options;
+  return smartlist_len(get_configured_client_ports()) > 0;
 }
 }
 
 
 /** Decide if we're a publishable server. We are a publishable server if:
 /** Decide if we're a publishable server. We are a publishable server if:

+ 3 - 0
src/or/routerparse.c

@@ -2092,6 +2092,7 @@ routerstatus_parse_entry_from_string(memarea_t *area,
       rs->version_supports_extrainfo_upload = 1;
       rs->version_supports_extrainfo_upload = 1;
       rs->version_supports_conditional_consensus = 1;
       rs->version_supports_conditional_consensus = 1;
       rs->version_supports_microdesc_cache = 1;
       rs->version_supports_microdesc_cache = 1;
+      rs->version_supports_optimistic_data = 1;
     } else {
     } else {
       rs->version_supports_begindir =
       rs->version_supports_begindir =
         tor_version_as_new_as(tok->args[0], "0.2.0.1-alpha");
         tor_version_as_new_as(tok->args[0], "0.2.0.1-alpha");
@@ -2109,6 +2110,8 @@ routerstatus_parse_entry_from_string(memarea_t *area,
        */
        */
       rs->version_supports_microdesc_cache =
       rs->version_supports_microdesc_cache =
         tor_version_as_new_as(tok->args[0], "0.2.3.0-alpha");
         tor_version_as_new_as(tok->args[0], "0.2.3.0-alpha");
+      rs->version_supports_optimistic_data =
+        tor_version_as_new_as(tok->args[0], "0.2.3.1-alpha");
     }
     }
     if (vote_rs) {
     if (vote_rs) {
       vote_rs->version = tor_strdup(tok->args[0]);
       vote_rs->version = tor_strdup(tok->args[0]);

+ 295 - 1
src/test/test.c

@@ -569,6 +569,71 @@ test_socks_5_auth_before_negotiation(void *ptr)
   ;
   ;
 }
 }
 
 
+static void
+test_buffer_copy(void *arg)
+{
+  generic_buffer_t *buf=NULL, *buf2=NULL;
+  const char *s;
+  size_t len;
+  char b[256];
+  int i;
+  (void)arg;
+
+  buf = generic_buffer_new();
+  tt_assert(buf);
+
+  /* Copy an empty buffer. */
+  tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+  tt_assert(buf2);
+  tt_int_op(0, ==, generic_buffer_len(buf2));
+
+  /* Now try with a short buffer. */
+  s = "And now comes an act of enormous enormance!";
+  len = strlen(s);
+  generic_buffer_add(buf, s, len);
+  tt_int_op(len, ==, generic_buffer_len(buf));
+  /* Add junk to buf2 so we can test replacing.*/
+  generic_buffer_add(buf2, "BLARG", 5);
+  tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+  tt_int_op(len, ==, generic_buffer_len(buf2));
+  generic_buffer_get(buf2, b, len);
+  test_mem_op(b, ==, s, len);
+  /* Now free buf2 and retry so we can test allocating */
+  generic_buffer_free(buf2);
+  buf2 = NULL;
+  tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+  tt_int_op(len, ==, generic_buffer_len(buf2));
+  generic_buffer_get(buf2, b, len);
+  test_mem_op(b, ==, s, len);
+  /* Clear buf for next test */
+  generic_buffer_get(buf, b, len);
+  tt_int_op(generic_buffer_len(buf),==,0);
+
+  /* Okay, now let's try a bigger buffer. */
+  s = "Quis autem vel eum iure reprehenderit qui in ea voluptate velit "
+    "esse quam nihil molestiae consequatur, vel illum qui dolorem eum "
+    "fugiat quo voluptas nulla pariatur?";
+  len = strlen(s);
+  for (i = 0; i < 256; ++i) {
+    b[0]=i;
+    generic_buffer_add(buf, b, 1);
+    generic_buffer_add(buf, s, len);
+  }
+  tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+  tt_int_op(generic_buffer_len(buf2), ==, generic_buffer_len(buf));
+  for (i = 0; i < 256; ++i) {
+    generic_buffer_get(buf2, b, len+1);
+    tt_int_op((unsigned char)b[0],==,i);
+    test_mem_op(b+1, ==, s, len);
+  }
+
+ done:
+  if (buf)
+    generic_buffer_free(buf);
+  if (buf2)
+    generic_buffer_free(buf2);
+}
+
 /** Run unit tests for buffers.c */
 /** Run unit tests for buffers.c */
 static void
 static void
 test_buffers(void)
 test_buffers(void)
@@ -1416,8 +1481,73 @@ static void
 test_geoip(void)
 test_geoip(void)
 {
 {
   int i, j;
   int i, j;
-  time_t now = time(NULL);
+  time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */
   char *s = NULL;
   char *s = NULL;
+  const char *bridge_stats_1 =
+      "bridge-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+      "bridge-ips zz=24,xy=8\n",
+  *dirreq_stats_1 =
+      "dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+      "dirreq-v3-ips ab=8\n"
+      "dirreq-v2-ips \n"
+      "dirreq-v3-reqs ab=8\n"
+      "dirreq-v2-reqs \n"
+      "dirreq-v3-resp ok=0,not-enough-sigs=0,unavailable=0,not-found=0,"
+          "not-modified=0,busy=0\n"
+      "dirreq-v2-resp ok=0,unavailable=0,not-found=0,not-modified=0,"
+          "busy=0\n"
+      "dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
+      "dirreq-v2-direct-dl complete=0,timeout=0,running=0\n"
+      "dirreq-v3-tunneled-dl complete=0,timeout=0,running=0\n"
+      "dirreq-v2-tunneled-dl complete=0,timeout=0,running=0\n",
+  *dirreq_stats_2 =
+      "dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+      "dirreq-v3-ips \n"
+      "dirreq-v2-ips \n"
+      "dirreq-v3-reqs \n"
+      "dirreq-v2-reqs \n"
+      "dirreq-v3-resp ok=0,not-enough-sigs=0,unavailable=0,not-found=0,"
+          "not-modified=0,busy=0\n"
+      "dirreq-v2-resp ok=0,unavailable=0,not-found=0,not-modified=0,"
+          "busy=0\n"
+      "dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
+      "dirreq-v2-direct-dl complete=0,timeout=0,running=0\n"
+      "dirreq-v3-tunneled-dl complete=0,timeout=0,running=0\n"
+      "dirreq-v2-tunneled-dl complete=0,timeout=0,running=0\n",
+  *dirreq_stats_3 =
+      "dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+      "dirreq-v3-ips \n"
+      "dirreq-v2-ips \n"
+      "dirreq-v3-reqs \n"
+      "dirreq-v2-reqs \n"
+      "dirreq-v3-resp ok=8,not-enough-sigs=0,unavailable=0,not-found=0,"
+          "not-modified=0,busy=0\n"
+      "dirreq-v2-resp ok=0,unavailable=0,not-found=0,not-modified=0,"
+          "busy=0\n"
+      "dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
+      "dirreq-v2-direct-dl complete=0,timeout=0,running=0\n"
+      "dirreq-v3-tunneled-dl complete=0,timeout=0,running=0\n"
+      "dirreq-v2-tunneled-dl complete=0,timeout=0,running=0\n",
+  *dirreq_stats_4 =
+      "dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+      "dirreq-v3-ips \n"
+      "dirreq-v2-ips \n"
+      "dirreq-v3-reqs \n"
+      "dirreq-v2-reqs \n"
+      "dirreq-v3-resp ok=8,not-enough-sigs=0,unavailable=0,not-found=0,"
+          "not-modified=0,busy=0\n"
+      "dirreq-v2-resp ok=0,unavailable=0,not-found=0,not-modified=0,"
+          "busy=0\n"
+      "dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
+      "dirreq-v2-direct-dl complete=0,timeout=0,running=0\n"
+      "dirreq-v3-tunneled-dl complete=0,timeout=0,running=4\n"
+      "dirreq-v2-tunneled-dl complete=0,timeout=0,running=0\n",
+  *entry_stats_1 =
+      "entry-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+      "entry-ips ab=8\n",
+  *entry_stats_2 =
+      "entry-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+      "entry-ips \n";
 
 
   /* Populate the DB a bit.  Add these in order, since we can't do the final
   /* 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
    * 'sort' step.  These aren't very good IP addresses, but they're perfectly
@@ -1467,6 +1597,114 @@ test_geoip(void)
   test_assert(s);
   test_assert(s);
   test_streq("zz=24,xy=8", s);
   test_streq("zz=24,xy=8", s);
 
 
+  /* Start testing bridge statistics by making sure that we don't output
+   * bridge stats without initializing them. */
+  s = geoip_format_bridge_stats(now + 86400);
+  test_assert(!s);
+
+  /* Initialize stats and generate the bridge-stats history string out of
+   * the connecting clients added above. */
+  geoip_bridge_stats_init(now);
+  s = geoip_format_bridge_stats(now + 86400);
+  test_streq(bridge_stats_1, s);
+  tor_free(s);
+
+  /* Stop collecting bridge stats and make sure we don't write a history
+   * string anymore. */
+  geoip_bridge_stats_term();
+  s = geoip_format_bridge_stats(now + 86400);
+  test_assert(!s);
+
+  /* Stop being a bridge and start being a directory mirror that gathers
+   * directory request statistics. */
+  geoip_bridge_stats_term();
+  get_options_mutable()->BridgeRelay = 0;
+  get_options_mutable()->BridgeRecordUsageByCountry = 0;
+  get_options_mutable()->DirReqStatistics = 1;
+
+  /* Start testing dirreq statistics by making sure that we don't collect
+   * dirreq stats without initializing them. */
+  geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, 100, now);
+  s = geoip_format_dirreq_stats(now + 86400);
+  test_assert(!s);
+
+  /* Initialize stats, note one connecting client, and generate the
+   * dirreq-stats history string. */
+  geoip_dirreq_stats_init(now);
+  geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, 100, now);
+  s = geoip_format_dirreq_stats(now + 86400);
+  test_streq(dirreq_stats_1, s);
+  tor_free(s);
+
+  /* Stop collecting stats, add another connecting client, and ensure we
+   * don't generate a history string. */
+  geoip_dirreq_stats_term();
+  geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, 101, now);
+  s = geoip_format_dirreq_stats(now + 86400);
+  test_assert(!s);
+
+  /* Re-start stats, add a connecting client, reset stats, and make sure
+   * that we get an all empty history string. */
+  geoip_dirreq_stats_init(now);
+  geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, 100, now);
+  geoip_reset_dirreq_stats(now);
+  s = geoip_format_dirreq_stats(now + 86400);
+  test_streq(dirreq_stats_2, s);
+  tor_free(s);
+
+  /* Note a successful network status response and make sure that it
+   * appears in the history string. */
+  geoip_note_ns_response(GEOIP_CLIENT_NETWORKSTATUS, GEOIP_SUCCESS);
+  s = geoip_format_dirreq_stats(now + 86400);
+  test_streq(dirreq_stats_3, s);
+  tor_free(s);
+
+  /* Start a tunneled directory request. */
+  geoip_start_dirreq((uint64_t) 1, 1024, GEOIP_CLIENT_NETWORKSTATUS,
+                     DIRREQ_TUNNELED);
+  s = geoip_format_dirreq_stats(now + 86400);
+  test_streq(dirreq_stats_4, s);
+
+  /* Stop collecting directory request statistics and start gathering
+   * entry stats. */
+  geoip_dirreq_stats_term();
+  get_options_mutable()->DirReqStatistics = 0;
+  get_options_mutable()->EntryStatistics = 1;
+
+  /* Start testing entry statistics by making sure that we don't collect
+   * anything without initializing entry stats. */
+  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, 100, now);
+  s = geoip_format_entry_stats(now + 86400);
+  test_assert(!s);
+
+  /* Initialize stats, note one connecting client, and generate the
+   * entry-stats history string. */
+  geoip_entry_stats_init(now);
+  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, 100, now);
+  s = geoip_format_entry_stats(now + 86400);
+  test_streq(entry_stats_1, s);
+  tor_free(s);
+
+  /* Stop collecting stats, add another connecting client, and ensure we
+   * don't generate a history string. */
+  geoip_entry_stats_term();
+  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, 101, now);
+  s = geoip_format_entry_stats(now + 86400);
+  test_assert(!s);
+
+  /* Re-start stats, add a connecting client, reset stats, and make sure
+   * that we get an all empty history string. */
+  geoip_entry_stats_init(now);
+  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, 100, now);
+  geoip_reset_entry_stats(now);
+  s = geoip_format_entry_stats(now + 86400);
+  test_streq(entry_stats_2, s);
+  tor_free(s);
+
+  /* Stop collecting entry statistics. */
+  geoip_entry_stats_term();
+  get_options_mutable()->EntryStatistics = 0;
+
  done:
  done:
   tor_free(s);
   tor_free(s);
 }
 }
@@ -1570,6 +1808,61 @@ test_stats(void)
   rep_hist_reset_conn_stats(now);
   rep_hist_reset_conn_stats(now);
   s = rep_hist_format_conn_stats(now + 86400);
   s = rep_hist_format_conn_stats(now + 86400);
   test_streq("conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,0,0\n", s);
   test_streq("conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,0,0\n", s);
+  tor_free(s);
+
+  /* Continue with testing buffer statistics; we shouldn't collect buffer
+   * stats without initializing them. */
+  rep_hist_add_buffer_stats(2.0, 2.0, 20);
+  s = rep_hist_format_buffer_stats(now + 86400);
+  test_assert(!s);
+
+  /* Initialize stats, add statistics for a single circuit, and generate
+   * the history string. */
+  rep_hist_buffer_stats_init(now);
+  rep_hist_add_buffer_stats(2.0, 2.0, 20);
+  s = rep_hist_format_buffer_stats(now + 86400);
+  test_streq("cell-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+             "cell-processed-cells 20,0,0,0,0,0,0,0,0,0\n"
+             "cell-queued-cells 2.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,"
+                               "0.00,0.00\n"
+             "cell-time-in-queue 2,0,0,0,0,0,0,0,0,0\n"
+             "cell-circuits-per-decile 1\n", s);
+  tor_free(s);
+
+  /* Add nineteen more circuit statistics to the one that's already in the
+   * history to see that the math works correctly. */
+  for (i = 21; i < 30; i++)
+    rep_hist_add_buffer_stats(2.0, 2.0, i);
+  for (i = 20; i < 30; i++)
+    rep_hist_add_buffer_stats(3.5, 3.5, i);
+  s = rep_hist_format_buffer_stats(now + 86400);
+  test_streq("cell-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+             "cell-processed-cells 29,28,27,26,25,24,23,22,21,20\n"
+             "cell-queued-cells 2.75,2.75,2.75,2.75,2.75,2.75,2.75,2.75,"
+                               "2.75,2.75\n"
+             "cell-time-in-queue 3,3,3,3,3,3,3,3,3,3\n"
+             "cell-circuits-per-decile 2\n", s);
+  tor_free(s);
+
+  /* Stop collecting stats, add statistics for one circuit, and ensure we
+   * don't generate a history string. */
+  rep_hist_buffer_stats_term();
+  rep_hist_add_buffer_stats(2.0, 2.0, 20);
+  s = rep_hist_format_buffer_stats(now + 86400);
+  test_assert(!s);
+
+  /* Re-start stats, add statistics for one circuit, reset stats, and make
+   * sure that the history has all zeros. */
+  rep_hist_buffer_stats_init(now);
+  rep_hist_add_buffer_stats(2.0, 2.0, 20);
+  rep_hist_reset_buffer_stats(now);
+  s = rep_hist_format_buffer_stats(now + 86400);
+  test_streq("cell-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+             "cell-processed-cells 0,0,0,0,0,0,0,0,0,0\n"
+             "cell-queued-cells 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,"
+                               "0.00,0.00\n"
+             "cell-time-in-queue 0,0,0,0,0,0,0,0,0,0\n"
+             "cell-circuits-per-decile 0\n", s);
 
 
  done:
  done:
   tor_free(s);
   tor_free(s);
@@ -1612,6 +1905,7 @@ const struct testcase_setup_t legacy_setup = {
 
 
 static struct testcase_t test_array[] = {
 static struct testcase_t test_array[] = {
   ENT(buffers),
   ENT(buffers),
+  { "buffer_copy", test_buffer_copy, 0, NULL, NULL },
   ENT(onion_handshake),
   ENT(onion_handshake),
   ENT(circuit_timeout),
   ENT(circuit_timeout),
   ENT(policies),
   ENT(policies),

+ 1 - 1
src/tools/tor-fw-helper/Makefile.am

@@ -34,5 +34,5 @@ miniupnpc_cppflags =
 endif
 endif
 
 
 tor_fw_helper_LDFLAGS = $(nat_pmp_ldflags) $(miniupnpc_ldflags)
 tor_fw_helper_LDFLAGS = $(nat_pmp_ldflags) $(miniupnpc_ldflags)
-tor_fw_helper_LDADD = $(nat_pmp_ldadd) $(miniupnpc_ldadd) ../../common/libor.a @TOR_LIB_WS32@
+tor_fw_helper_LDADD = ../../common/libor.a $(nat_pmp_ldadd) $(miniupnpc_ldadd) @TOR_LIB_WS32@
 tor_fw_helper_CPPFLAGS = $(nat_pmp_cppflags) $(miniupnpc_cppflags)
 tor_fw_helper_CPPFLAGS = $(nat_pmp_cppflags) $(miniupnpc_cppflags)

+ 10 - 0
src/win32/orconfig.h

@@ -122,6 +122,7 @@
 /* Define to 1 if you have the <sys/socket.h> header file. */
 /* Define to 1 if you have the <sys/socket.h> header file. */
 #undef HAVE_SYS_SOCKET_H
 #undef HAVE_SYS_SOCKET_H
 
 
+
 /* Define to 1 if you have the <sys/stat.h> header file. */
 /* Define to 1 if you have the <sys/stat.h> header file. */
 #define HAVE_SYS_STAT_H
 #define HAVE_SYS_STAT_H
 
 
@@ -234,3 +235,12 @@
 
 
 /* Version number of package */
 /* Version number of package */
 #define VERSION "0.2.3.2-alpha-dev"
 #define VERSION "0.2.3.2-alpha-dev"
+
+
+
+#define HAVE_STRUCT_SOCKADDR_IN6
+#define HAVE_STRUCT_IN6_ADDR
+#define RSHIFT_DOES_SIGN_EXTEND
+#define FLEXIBLE_ARRAY_MEMBER 0
+#define HAVE_EVENT2_EVENT_H
+#define SHARE_DATADIR ""

Some files were not shown because too many files changed in this diff