[or-cvs] r11079: added replication of descriptors, cleaned up the code, last (in tor/branches/114-dist-storage: doc/spec doc/spec/proposals src/common src/or)

kloesing at seul.org kloesing at seul.org
Sat Aug 11 22:30:45 UTC 2007


Author: kloesing
Date: 2007-08-11 18:30:44 -0400 (Sat, 11 Aug 2007)
New Revision: 11079

Modified:
   tor/branches/114-dist-storage/doc/spec/control-spec.txt
   tor/branches/114-dist-storage/doc/spec/dir-spec.txt
   tor/branches/114-dist-storage/doc/spec/proposals/114-distributed-storage.txt
   tor/branches/114-dist-storage/doc/spec/rend-spec.txt
   tor/branches/114-dist-storage/src/common/crypto.c
   tor/branches/114-dist-storage/src/or/circuitlist.c
   tor/branches/114-dist-storage/src/or/circuituse.c
   tor/branches/114-dist-storage/src/or/config.c
   tor/branches/114-dist-storage/src/or/connection_edge.c
   tor/branches/114-dist-storage/src/or/directory.c
   tor/branches/114-dist-storage/src/or/dirserv.c
   tor/branches/114-dist-storage/src/or/main.c
   tor/branches/114-dist-storage/src/or/or.h
   tor/branches/114-dist-storage/src/or/rendclient.c
   tor/branches/114-dist-storage/src/or/rendcommon.c
   tor/branches/114-dist-storage/src/or/rendmid.c
   tor/branches/114-dist-storage/src/or/rendservice.c
   tor/branches/114-dist-storage/src/or/router.c
   tor/branches/114-dist-storage/src/or/routerlist.c
   tor/branches/114-dist-storage/src/or/routerparse.c
   tor/branches/114-dist-storage/src/or/test.c
Log:
added replication of descriptors, cleaned up the code, last merge with trunk

Modified: tor/branches/114-dist-storage/doc/spec/control-spec.txt
===================================================================
--- tor/branches/114-dist-storage/doc/spec/control-spec.txt	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/doc/spec/control-spec.txt	2007-08-11 22:30:44 UTC (rev 11079)
@@ -62,18 +62,26 @@
 
 2.3. Replies from Tor to the controller
 
-    Reply = *(MidReplyLine / DataReplyLine) EndReplyLine
+    Reply = SyncReply / AsyncReply
+    SyncReply = *(MidReplyLine / DataReplyLine) EndReplyLine
+    AsyncReply = *(MidReplyLine / DataReplyLine) EndReplyLine
 
-    MidReplyLine = "-" ReplyLine
-    DataReplyLine = "+" ReplyLine Data
-    EndReplyLine = SP ReplyLine
-    ReplyLine = StatusCode [ SP ReplyText ]  CRLF
+    MidReplyLine = StatusCode "-" ReplyLine
+    DataReplyLine = StatusCode "+" ReplyLine Data
+    EndReplyLine = StatusCode SP ReplyLine
+    ReplyLine = [ReplyText] CRLF
     ReplyText = XXXX
-    StatusCode = XXXX
+    StatusCode = 3DIGIT
 
   Specific replies are mentioned below in section 3, and described more fully
   in section 4.
 
+  [Compatibility note:  versions of Tor before 0.2.0.3-alpha sometimes
+  generate AsyncReplies of the form "*(MidReplyLine / DataReplyLine)".
+  This is incorrect, but controllers that need to work with these
+  versions of Tor should be prepared to get multi-line AsyncReplies with
+  the final line (usually "650 OK") omitted.]
+
 2.4. General-use tokens
 
   ; Identifiers for servers.
@@ -217,15 +225,19 @@
      "AUTHENTICATE" [ SP 1*HEXDIG / QuotedString ] CRLF
 
   The server responds with "250 OK" on success or "515 Bad authentication" if
-  the authentication cookie is incorrect.
+  the authentication cookie is incorrect.  Tor closes the connection on an
+  authentication failure.
 
   The format of the 'cookie' is implementation-dependent; see 5.1 below for
   information on how the standard Tor implementation handles it.
 
   If Tor requires authentication and the controller has not yet sent an
   AUTHENTICATE message, Tor sends a "514 authentication required" reply to
-  any other kind of message.
+  any other kind of message, and then closes the connection.
 
+  (Versions of Tor before 0.1.2.16 and 0.2.0.4-alpha did not close the
+  connection after an authentication failure.)
+
 3.6. SAVECONF
 
   Sent from the client to the server.  The syntax is:
@@ -377,17 +389,24 @@
       space-separated list of LongName, each preceded by a "!" if it is
       believed to be not running.)
 
-    "addr-mappings/all"
-    "addr-mappings/config"
-    "addr-mappings/cache"
-    "addr-mappings/control" -- a \r\n-separated list of address
-      mappings, each in the form of "from-address to-address".
+    "address-mappings/all"
+    "address-mappings/config"
+    "address-mappings/cache"
+    "address-mappings/control" -- a \r\n-separated list of address
+      mappings, each in the form of "from-address to-address expiry".
       The 'config' key returns those address mappings set in the
       configuration; the 'cache' key returns the mappings in the
       client-side DNS cache; the 'control' key returns the mappings set
       via the control interface; the 'all' target returns the mappings
       set through any mechanism.
+      Expiry is formatted as with ADDRMAP events, except that "expiry" is
+      always a time in GMT or the string "NEVER"; see section 4.1.7.
+      First introduced in 0.2.0.3-alpha.
 
+    "addr-mappings/*" -- as for address-mappings/*, but without the
+      expiry portion of the value.  Use of this value is deprecated
+      since 0.2.0.3-alpha; use address-mappings instead.
+
     "address" -- the best guess at our external IP address. If we
       have no guess, return a 551 error. (Added in 0.1.2.2-alpha)
 
@@ -489,6 +508,12 @@
       states. See Section 4.1.10 for explanations. (Only a few of the
       status events are available as getinfo's currently. Let us know if
       you want more exposed.)
+    "status/version/recommended" -- List of currently recommended versions
+    "status/version/current" -- Status of the current version. One of:
+        new, old, unrecommended, recommended, new in series, obsolete.
+    "status/version/num-versioning" -- Number of versioning authorities
+    "status/version/num-concurring" -- Number of versioning authorities
+        agreeing on the status of the current version
 
   Examples:
      C: GETINFO version desc/name/moria1
@@ -904,7 +929,7 @@
 
   If extended events are enabled (see 3.19), optional reason and
   circuit counting information is provided for CLOSED and FAILED
-  events. 
+  events.
 
       Reason = "MISC" / "DONE" / "CONNECTREFUSED" /
                "IDENTITY" / "CONNECTRESET" / "TIMEOUT" / "NOROUTE" /
@@ -930,7 +955,7 @@
   The syntax is:
      "650" SP Severity SP ReplyText
   or
-     "650+" Severity CRLF Data
+     "650+" Severity CRLF Data 650 SP "OK" CRLF
 
      Severity = "DEBUG" / "INFO" / "NOTICE" / "WARN"/ "ERR"
 
@@ -942,11 +967,18 @@
 4.1.7. New Address mapping
 
   Syntax:
-     "650" SP "ADDRMAP" SP Address SP Address SP Expiry
+     "650" SP "ADDRMAP" SP Address SP Address SP Expiry SP Error SP GMTExpiry
      Expiry = DQUOTE ISOTime DQUOTE / "NEVER"
 
-  Expiry is expressed as the local time (rather than GMT).
+     Error = / "error=" ErrorCode
+     GMTExpiry = "EXPIRES=" DQUOTE IsoTime DQUOTE
 
+  Error and GMTExpiry are only provided if extended events are enabled.
+
+  Expiry is expressed as the local time (rather than GMT).  This is a bug,
+  left in for backward compatibility; new code should look at GMTExpiry
+  instead.
+
   [XXX We should rename this to ADDRESSMAP. -RD]
     [Why? Surely it can't be worth the compatibility issues. -NM]
 
@@ -954,7 +986,7 @@
 
   Syntax:
      "650" "+" "AUTHDIR_NEWDESCS" CRLF Action CRLF Message CRLF
-       Descriptor CRLF "." CRLF
+       Descriptor CRLF "." CRLF "650" SP "OK" CRLF
      Action = "ACCEPTED" / "DROPPED" / "REJECTED"
      Message = Text
 
@@ -1285,7 +1317,7 @@
 4.1.12. Network status has changed
 
   Syntax:
-     "650" "+" "NS" CRLF  1*NetworkStatus "." CRLF
+     "650" "+" "NS" CRLF  1*NetworkStatus "." CRLF "650" SP "OK" CRLF
 
   [First added in 0.1.2.3-alpha]
 
@@ -1336,16 +1368,16 @@
 
 5.3. Backward compatibility with v0 control protocol.
 
-  For backward compatibility with the "version 0" control protocol, Tor checks
-  whether the third octet the first command is zero.  If it is, Tor
-  assumes that version 0 is in use.  This feature is deprecated, and will be
-  removed in the 0.1.3.x Tor development series.
+  The 'version 0' control protocol was replaced in Tor 0.1.1.x.  Support was
+  removed in Tor 0.2.0.x.  Every non-obsolete version of Tor now supports the
+  version 1 control protocol.  
 
-  In order to detect which version of the protocol is supported controllers
-  should send the sequence [00 00 0D 0A].  This is a valid and unrecognized
-  command in both protocol versions, and implementations can detect which
-  error they have received.
+  For backward compatibility with the "version 0" control protocol,
+  Tor used to check whether the third octet of the first command is zero.
+  (If it was, Tor assumed that version 0 is in use.)
 
+  This compatibility was removed in Tor 0.1.2.16 and 0.2.0.4-alpha.
+
 5.4. Options for use by controllers
 
   Tor provides a few special configuration options for use by controllers.

Modified: tor/branches/114-dist-storage/doc/spec/dir-spec.txt
===================================================================
--- tor/branches/114-dist-storage/doc/spec/dir-spec.txt	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/doc/spec/dir-spec.txt	2007-08-11 22:30:44 UTC (rev 11079)
@@ -297,6 +297,29 @@
    The "Digest" of a document, unless stated otherwise, is its digest *as
    signed by this signature scheme*.
 
+1.4. Voting timeline
+
+   Every consensus document has a "valid-after" (VA) time, a "fresh-until"
+   (FU) time and a "valid-until" (VU) time.  VA MUST precede FU, which MUST
+   in turn precede VU.  Times are chosen so that every consensus will be
+   "fresh" until the next consensus becomes valid, and "valid" for a while
+   after.  At least 2 or 3 consensuses should be valid at any given time.
+
+   The timeline for a given consensus is as follows:
+
+   VA-DistSeconds-VoteSeconds: The authorities exchange votes.
+
+   VA-DistSeconds: The authorities calculate the consensus and exchange
+   signatures.
+
+   VA: All authorities have a multiply signed consensus.
+
+   VA ... FU: Caches download the consensus.
+
+   FU: The consensus is no long the freshest consensus.
+
+   VU: The consensus is no longer valid.
+
 2. Router operation and formats
 
    ORs SHOULD generate a new router descriptor and a new extra-info
@@ -708,6 +731,14 @@
         The status MUST be "vote" or "consensus", depending on the type of
         the document.
 
+    "consensus-methods" SP IntegerList NL
+
+        [Exactly once for votes; does not occur in consensuses.]
+
+        A space-separated list of supported methods for generating
+        consensuses from votes.  See section 3.4.1 for details.  Method "1"
+        MUST be included.
+
     "published" SP YYYY-MM-DD SP HH:MM:SS NL
 
         [Exactly once for votes; does not occur in consensuses.]
@@ -718,25 +749,34 @@
 
         [Exactly once.]
 
-        The start of the Interval for this vote. XXXX
+        The start of the Interval for this vote.  Before this time, the
+        consensus document produced from this vote should not be used.
+        See 1.4 for voting timeline information.
 
     "fresh-until" SP YYYY-MM-DD SP HH:MM:SS NL
 
         [Exactly once.]
 
-        XXXX
+        The time at which the next consensus should be produced; before this
+        time, there is no point in downloading another consensus, since there
+        won't be a new one.  See 1.4 for voting timeline information.
 
     "valid-until" SP YYYY-MM-DD SP HH:MM:SS NL
 
         [Exactly once.]
 
-        The end of the Interval for this vote. XXXX
+        The end of the Interval for this vote.  After this time, the
+        consensus produced by this vote should not be used.  See 1.4 for
+        voting timeline information.
 
     "voting-delay" SP VoteSeconds SP DistSeconds NL
 
         [Exactly once.]
 
-        XXXX
+        VoteSeconds is the number of seconds that we will allow to collect
+        votes from all authorities; DistSeconds is the number of seconds
+        we'll allow to collect signatures from all authorities. See 1.4 for
+        voting timeline information.
 
     "client-versions" SP VersionList NL
 
@@ -911,19 +951,19 @@
    it successfully within the last 30 minutes.
 
    "Stable" -- A router is 'Stable' if it is active, and either its
-   uptime is at least the median uptime for known active routers, or
+   uptime is at least the median uptime for known active routers or
    its uptime is at least 30 days. Routers are never called stable if
    they are running a version of Tor known to drop circuits stupidly.
    (0.1.1.10-alpha through 0.1.1.16-rc are stupid this way.)
 
    "Fast" -- A router is 'Fast' if it is active, and its bandwidth is
-   in the top 7/8ths for known active routers.
+   either in the top 7/8ths for known active routers or at least 100KB/s.
 
    "Guard" -- A router is a possible 'Guard' if it is 'Stable' and its
-   bandwidth is above median for known active routers. If the total
-   bandwidth of active non-BadExit Exit servers is less than one third
-   of the total bandwidth of all active servers, no Exit is listed as
-   a Guard.
+   bandwidth is either at least the median for known active routers or
+   at least 250KB/s. If the total bandwidth of active non-BadExit Exit
+   servers is less than one third of the total bandwidth of all active
+   servers, no Exit is listed as a Guard.
 
    "Authority" -- A router is called an 'Authority' if the authority
    generating the network-status document believes it is an authority.
@@ -945,7 +985,13 @@
    Directory server administrators may label some servers or IPs as
    blacklisted, and elect not to include them in their network-status lists.
 
-   Thus, the network-status list includes all non-blacklisted,
+   Authorities SHOULD 'disable' any servers in excess of 3 on any single IP.
+   When there are more than 3 to choose from, authorities should first prefer
+   authorities to non-authorities, then prefer Running to non-Running, and
+   then prefer high-bandwidth to low-bandwidth.  To 'disable' a server, the
+   authority *should* advertise it without the Running or Valid flag.
+
+   Thus, the network-status vote includes all non-blacklisted,
    non-expired, non-superseded descriptors.
 
 3.4. Computing a consensus from a set of votes
@@ -997,6 +1043,22 @@
      The signatures at the end of a consensus document are sorted in
      ascending order by identity digest.
 
+3.4.1. Forward compatibility
+
+   Future versions of Tor will need to include new information in the
+   consensus documents, but it is important that all authorities (or at least
+   half) generate and sign the same signed consensus.
+
+   To achieve this, authorities list in their votes their supported methods
+   for generating consensuses from votes.  The method described above and
+   implemented in Tor 0.2.0.x is "1".  Later methods will be assigned higher
+   numbers.
+
+   Before generating a consensus, an authority must decide which consensus
+   method to use.  To do this, it looks for the highest version number
+   supported by more than 2/3 of the authorities.  If it supports this
+   method, then it uses it.  Otherwise, it falls back to method 1.
+
 3.5. Detached signatures
 
    Assuming full connectivity, every authority should compute and sign the
@@ -1078,17 +1140,15 @@
    merged with the second-to-last period.
 
    An authority SHOULD publish its vote immediately at the start of each voting
-   period.  It does this by making it available at
+   period (minus VoteSeconds+DistSeconds).  It does this by making it
+   available at
      http://<hostname>/tor/status-vote/current/authority.z
    and sending it in an HTTP POST request to each other authority at the URL
      http://<hostname>/tor/post/vote
 
-   (Note that this requires the authority to settle upon and finalize its
-   vote slightly before the start of the voting period.)
-
-   If, VOTING_DELAY minutes after the voting period has begun, an authority
+   If, at the start of the voting period, minus DistSeconds, an authority
    does not have a current statement from another authority, the first
-   authority retrieves the other's statement.
+   authority downloads the other's statement.
 
    Once an authority has a vote from another authority, it makes it available
    at
@@ -1097,9 +1157,15 @@
 
    The consensus status, along with as many signatures as the server
    currently knows, should be available at
-      http://<hostname>/tor/status-vote/current/consensus.z
+      http://<hostname>/tor/status-vote/next/consensus.z
    All of the detached signatures it knows for consensus status should be
    available at:
+      http://<hostname>/tor/status-vote/next/consensus-signatures.z
+
+   Once there are enough signatures, or once the voting period starts,
+   these documents are available at
+      http://<hostname>/tor/status-vote/current/consensus.z
+   and
       http://<hostname>/tor/status-vote/current/consensus-signatures.z
 
    Once an authority has computed and signed a consensus network status, it
@@ -1112,18 +1178,18 @@
    [XXX possible future features include support for downloading old
     consensuses.]
 
-   [XXX Constants: VOTING_DELAY, CONSENSUS_DELAY]
-
-
 4.3. Downloading consensus status documents (caches only)
 
-   All directory servers (authorities and caches) try to keep a fresh
-   set of network-status consensus documents to serve to clients.  Every
-   15 minutes, or whenever the valid-until field on its most current
-   consensus is about to expire
+   All directory servers (authorities and caches) try to keep a recent
+   network-status consensus document to serve to clients.  A cache ALWAYS
+   downloads a network-status consensus if any of the following are true:
+     - The cache has no consensus document.
+     - The cache's consensus document is no longer valid.
+   Otherwise, the cache downloads a new consensus document at a randomly
+   chosen time after its current consensus stops being fresh.  (This time is
+   chosen at random to avoid swarming the authorities at the start of each
+   period.)
 
-[XXXX finish this section]
-
 4.4. Downloading and storing router descriptors (authorities and caches)
 
    Periodically (currently, every 10 seconds), directory servers check
@@ -1268,14 +1334,12 @@
    until it has a live network-status consensus document, and it has
    descriptors for more than 1/4 of the routers that it believes are running.
 
- [XXXX handling clock skew at client side?]
- [XXXX fall-back to most recent?]
-
    (Note: clients can and should pick caches based on the network-status
    information they have: once they have first fetched network-status info
    from an authority, they should not need to go to the authority directly
    again.)
 
+
 5.2. Downloading and storing router descriptors
 
    Clients try to have the best descriptor for each router.  A descriptor is

Modified: tor/branches/114-dist-storage/doc/spec/proposals/114-distributed-storage.txt
===================================================================
--- tor/branches/114-dist-storage/doc/spec/proposals/114-distributed-storage.txt	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/doc/spec/proposals/114-distributed-storage.txt	2007-08-11 22:30:44 UTC (rev 11079)
@@ -1,7 +1,7 @@
 Filename: 114-distributed-storage.txt
 Title: Distributed Storage for Tor Hidden Service Descriptors
-Version: $Revision: 10773 $
-Last-Modified: $Date: 2007-07-09 16:54:48 +0200 (Mo, 09 Jul 2007) $
+Version: $Revision: 10821 $
+Last-Modified: $Date: 2007-07-12 20:06:13 +0200 (Do, 12 Jul 2007) $
 Author: Karsten Loesing
 Created: 13-May-2007
 Status: Open
@@ -9,17 +9,20 @@
 Change history:
 
   13-May-2007  Initial proposal
-  14-May-2007  Added changes suggested by Lasse Overlier
+  14-May-2007  Added changes suggested by Lasse Øverlier
   30-May-2007  Changed descriptor format, key length discussion, typos
   09-Jul-2007  Incorporated suggestions by Roger, added status of specification
                and implementation for upcoming GSoC mid-term evaluation
+  11-Aug-2007  Updated implementation statuses, included non-consecutive
+               replication to descriptor format
 
 Overview:
 
   The basic idea of this proposal is to distribute the tasks of storing and
   serving hidden service descriptors from currently three authoritative
-  directory nodes among a large subset of all onion routers. The two reasons to
-  do this are better scalability and improved security properties. Further,
+  directory nodes among a large subset of all onion routers. The three
+  reasons to do this are better robustness (availability), better
+  scalability, and improved security properties. Further,
   this proposal suggests changes to the hidden service descriptor format to
   prevent new security threats coming from decentralization and to gain even
   better security properties.
@@ -41,7 +44,7 @@
   increasing number of replicas. These scalability issues have an impact on the
   current usage of hidden services and put an even higher burden on the
   development of new kinds of applications for hidden services that might
-  require storing even bigger numbers of descriptors.
+  require storing even more descriptors.
 
   Second, besides posing a limitation to scalability, storing all hidden
   service descriptors on three directory nodes also constitutes a security
@@ -98,7 +101,7 @@
   descriptor. It is used to calculate descriptor IDs and to encrypt the
   introduction points. This second key can either be given to all clients
   together with the hidden service ID, or to a group or a single client as
-  authentication token. In the future this second key could be the result of
+  an authentication token. In the future this second key could be the result of
   some key agreement protocol between the hidden service and one or more
   clients. A new text-based format is proposed for descriptors instead of an
   extension of the existing binary format for reasons of future extensibility.
@@ -127,8 +130,13 @@
     - routerlist.c: Changed router_get_routerlist() to initialize routing list.
     - or.h: Added hs_dirs member to routerlist_t.
 
-      [July 9: Specified and running, though the routing list is compiled for
-       each request anew.]
+    - Changed routerlist_free() to free storage held by routing list.
+    - Added UPDATE_HS_DIRS_INTERVAL.
+    - Added update_hs_dir_routing_table().
+    - Changed run_scheduled_events().
+    - Added is_hs_dir member to routerstatus_t.
+    
+      [Aug 11: Specified and running.]
 
   /2/ Determine responsible hidden service directory
 
@@ -143,11 +151,13 @@
     - rend-spec.txt, section 1.4: Added description of how to determine the
       responsible node(s) for a given descriptor ID.
 
-    - routerlist.c: Added get_responsible_hs_dir() to determine the router that
-      is responsible for a given descriptor ID.
-    - container.h: Added prototype for smartlist_digest_next_circular().
-    - container.c: Added implementation for smartlist_digest_next_circular().
-
+    - routerlist.c: Added get_responsible_hs_dirs() to determine the routers
+      that are responsible for a given descriptor ID.
+      
+    - Added is_hs_dir member to routerstatus_t.
+    - Added have_enough_hs_dirs().
+    - Added next_hs_dir().
+    
       [July 9: Specified and running.]
     
   Hidden service clients and providers:
@@ -219,20 +229,19 @@
     - routerparse.c: Added 8 keywords to directory_keyword to parse v2 hidden
       service descriptors.
     - rendcommon.c: Added rend_cache_store_v2_dir() to allow a hidden service
-      directory to store a v2 descriptor in the local cache under its
-      descriptor ID instead of its service ID.
-    - rendcommon.c: Moved the parsing part from rend_cache_store() to the new
-      function rend_cache_store_parse() to reuse it for v2 descriptors.
+      directory to parse a v2 descriptor and store it in the local cache under
+      its descriptor ID instead of its service ID.
     - or.h: Added constant REND_DESC_ID_V2_LEN to reflect that v2 descriptor
       IDs are longer than v0/1 onion addresses.
 
-      [July 9: Base version specified and running; no checking of published
-       descriptors, tunneling over BEGIN_DIR cells not yet implemented.]
+    - Changed directory_handle_command_post().
+    
+      [Aug 11: Specified and running.]
 
   /7/ Accept v2 fetch requests
 
     Same as /6/, but with fetch requests for hidden service descriptors.
-    (requires /4/)
+    (requires /2/ and /4/)
 
     - rend-spec.txt, section 3.3: Added the processing of v2 fetch requests.
 
@@ -242,8 +251,9 @@
     - or.h: Added constant REND_DESC_ID_V2_LEN to reflect that v2 descriptor
       IDs are longer than v0/1 onion addresses.
 
-      [July 9: Base version specified and running; tunneling over BEGIN_DIR
-       cells not yet implemented.]
+    - Changed directory_handle_command_get().
+    
+      [Aug 11: Specified and running.]
 
   /8/ Replicate descriptors with neighbors
 
@@ -260,8 +270,19 @@
 
     - rend-spec.txt, section 3.3: Added the replication of v2 descriptors.
 
-      [July 9: To some extend specified, but not yet implemented.]
+    - Added HS_DIR_REPLICATION_INTERVAL.
+    - Added next_hs_dir and previous_hs_dir.
+    - Changed directory_handle_command_get().
+    - Changed run_scheduled_events.
+    - Added hs_dir_perform_replication().
+    - Added rend_cache_lookup_v2_replicas.
+    - Added DIR_PURPOSE_REPLICATE_RENDDESC_V2.
+    - Changed directory_initiate_command.
+    - directory_send_command.
+    - Changed connection_dir_client_reached_eof.
 
+      [Aug 11: To some extend specified, running.]
+
   Authoritative directory nodes:
 
   /9/ Confirm a router's hidden service directory functionality
@@ -285,16 +306,17 @@
       "hidden-service-directory" flag in router descriptors.
     - routerparse.c: Added 1 keyword to directory_keyword to parse the
       "hidden-service-dir" flag in router descriptors.
-    - or.h: Added is_hs_dir member to routerinfo_t and to routerstatus_t.
+    - or.h: Added is_hs_dir and wants_to_be_hs_dir members to routerinfo_t.
     - dirserv.c: Changed routerstatus_format_entry() to include the "HSDir"
       flag in vote and consensus status documents.
     - dirserv.c: Changed set_routerstatus_from_routerinfo() to set the "HSDir"
       flag.
 
-      [July 9: Base version specified and running in which all nodes that have
-       the hidden-service-dir flag set in their router descriptor get the
-       HSDir flag, not only those which are running for at least 24 hours.]
+    - Added dirserv_thinks_router_is_hs_dir().
+    - Added MIN_UPTIME_HS_DIR and HS_DIR_REACHABLE_TIMEOUT.
 
+      [Aug 11: Specified and running.]
+
   Hidden service provider:
 
   /10/ Configure v2 hidden service
@@ -338,6 +360,8 @@
       service provider uses a freshly generated public key for every
       introduction point.
 
+    - TODO: Change in rend_encode_v2_descriptors.
+
       [July 9: Specified, but not yet implemented.]
 
   /12/ Encode v2 descriptors and send v2 publish requests
@@ -351,7 +375,7 @@
     the next period. Publication is performed by sending the descriptor to all
     hidden service directories that are responsible for keeping replicas for
     the descriptor ID. This includes two non-consecutive replicas that are
-    stored at 3 consecutive nodes each. (requires /1/ and /3/)
+    stored at 3 consecutive nodes each. (requires /1/, /2/, and /3/)
 
     - rend-spec.txt, section 1.2: Added the new v2 hidden service descriptor
       format.
@@ -364,25 +388,20 @@
     - rendservice.c: Changed rend_consider_services_upload() to also initiate
       the upload of v2 descriptors, if configured.
     - rendservice.c: Extended rend_service_t by a member secret_cookie.
-    - rendcommon.c: Added rend_compute_v2_descriptor_fields() to prepare the
-      encoding of a v2 descriptor.
     - rendcommon.c: Added rend_encode_v2_descriptor() to encode a v2
       descriptor.
-    - or.h: Added 7 new members to rend_service_descriptor_t to store
-      v2-specific information.
     - or.h: Added constant DIR_PURPOSE_UPLOAD_RENDDESC_V2.
     - directory.c: Added directory_post_to_hs_dir().
     - directory.c: Changed directory_initiate_command() to also recognize v2
       publish requests.
     - directory.c: Changed directory_send_command() to also prepare v2 publish
       requests.
-    - directory.c: Changed directory_handle_command_post() to handle v2 publish
-      requests.
     - crypto.c: Added implementation for crypto_cipher_encrypt_cbc().
 
-      [July 9: Base version specified and running; yet, replication is not
-       implemented, republication does not depend on publication periods, yet.]
+    - Changed connection_dir_client_reached_eof().
 
+      [Aug 11: Specified and running.]
+
   Hidden service client:
 
   /13/ Send v2 fetch requests
@@ -406,10 +425,10 @@
 
     - rendcommon.c: Changed rend_cache_lookup_entry to enable it to also lookup
       v2 descriptors.
-    - rendcommon.c: Added rend_compute_desc_id() to generate v2 descriptor IDs
+    - rendcommon.c: Added rend_compute_v2_desc_id() to generate v2 descriptor IDs
       from v2 onion addresses.
     - rendcommon.c: Changed rend_valid_service_id() to also consider v2 onion
-      addresses as valid and return the version number of the request (1 or 2).
+      addresses as valid and return the version number of the request (0 or 2).
     - rendclient.c: Added rend_client_refetch_v2_renddesc() to fetch v2 service
       descriptors using the secret cookie.
     - rendclient.c: Changed rend_client_remove_intro_point() to copy the secret
@@ -424,16 +443,14 @@
       fetch requests.
     - directory.c: Changed directory_send_command() to also prepare v2 fetch
       requests.
-    - directory.c: Changed directory_handle_command_get() to handle v2 fetch
-      requests.
     - connection_edge.c: Changed connection_ap_handshake_rewrite_and_attach()
       to fetch v2 service descriptors.
     - connection_edge.c: Changed parse_extended_hostname() to accept both,
       current and v2 onion addresses.
     - config.c: Added config options FetchV2HidServDescriptors.
 
-      [July 9: Base version specified and running in which only one node is
-       responsible for a specific descriptor ID.]
+      [Aug 11: Base version specified and running, but no memory of failed
+       hidden service directories, yet.]
 
   /14/ Process v2 fetch reply and parse v2 descriptors
 
@@ -453,15 +470,14 @@
       introduction points of v2 hidden service descriptors.
     - routerparse.c: Added desc_token_table[] to parse v2 hidden service
       descriptors.
-    - routerparse.c: Added 8 to directory_keyword to parse v2 hidden service
-      descriptors, and 5 to parse the decrypted list of introduction points.
+    - routerparse.c: Added 8 keywords to directory_keyword to parse v2 hidden
+      service descriptors, and 5 to parse the decrypted list of introduction
+      points.
     - rendcommon.c: Added rend_cache_store_v2_client() to parse a v2 descriptor
       and parse the encrypted list of introduction points.
-    - or.h: Added secret_cookie to edge_connection_t, to dir_connection_t, and
-      to origin_circuit_t to be able to decrypt introduction points when
-      receiving a v2 descriptor.
-    - or.h: Added 7 new members to rend_service_descriptor_t to store
-      v2-specific information.
+    - or.h: Added rend_version and secret_cookie to edge_connection_t, to
+      dir_connection_t, and to origin_circuit_t to be able to decrypt
+      introduction points when receiving a v2 descriptor.
     - directory.c: Changed connection_dir_client_reached_eof() to also parse v2
       fetch replies.
     - crypto.c: Added implementation for crypto_cipher_decrypt_cbc().
@@ -491,8 +507,6 @@
     - or.h: Added secret_cookie to edge_connection_t, to dir_connection_t, and
       to origin_circuit_t to be able to decrypt introduction points when
       receiving a v2 descriptor.
-    - or.h: Added 7 new members to rend_service_descriptor_t to store
-      v2-specific information.
     - circuitlist.c: Changed _circuit_mark_for_close() to pass the secret
       cookie to rend_client_remove_intro_point() when an intro circ has failed.
     - circuituse.c: Changed circuit_get_open_circ_or_launch() to fetch a v2
@@ -509,12 +523,12 @@
     The new v2 hidden service descriptor format looks like this:
 
       onion-address = h(public-key) + cookie
-      descriptor-id = h(h(public-key) + h(time-period + cookie))
+      descriptor-id = h(h(public-key) + h(time-period + cookie + relica))
       descriptor-content = {
         descriptor-id,
         version,
         public-key,
-        h(time-period + cookie),
+        h(time-period + cookie + replica),
         timestamp,
         protocol-versions,
         { introduction-points } encrypted with cookie
@@ -530,13 +544,14 @@
     
     Therefore, "descriptor-id" is derived from the "public-key" of the hidden
     service provider, the current "time-period" which changes every 24 hours,
-    and a secret "cookie" shared between hidden service provider and clients.
-    (The "time-period" is constructed in a way that time periods do not change
-    at the same moment for all descriptors by deriving a value between 0:00 and
-    23:59 hours from "public-key" and making the descriptors of this hidden
+    a secret "cookie" shared between hidden service provider and clients, and
+    a "replica" denoting the number of this non-consecutive replica. (The
+    "time-period" is constructed in a way that time periods do not change at
+    the same moment for all descriptors by deriving a value between 0:00 and
+    23:59 hours from h(public-key) and making the descriptors of this hidden
     service provider expire at that time of the day.) The "descriptor-id" is
     defined to be 160 bits long. [extending the "descriptor-id" length
-    suggested by LO]
+    suggested by LØ]
     
     Only the hidden service provider and the clients are able to generate
     future "descriptor-ID"s. Hence, the "onion-address" is extended from now 
@@ -555,7 +570,7 @@
     The "introduction-points" that are included in the descriptor are encrypted
     using the same "cookie" that is shared between hidden service provider and
     clients. [correction to use another key than h(time-period + cookie) as
-    encryption key for introduction points made by LO]
+    encryption key for introduction points made by LØ]
 
     A new text-based format is proposed for descriptors instead of an extension
     of the existing binary format for reasons of future extensibility.
@@ -567,7 +582,7 @@
 
   Attacks by authoritative directory nodes
 
-    Authoritative directory nodes are not anymore the single places in the
+    Authoritative directory nodes are no longer the single places in the
     network that know about a hidden service's activity and introduction
     points. Thus, they cannot perform attacks using this information, e.g.
     track a hidden service's activity or usage pattern or attack its
@@ -584,7 +599,8 @@
     to have a good probability to become responsible for its changing
     descriptor IDs. For each period, the probability is:
 
-      1-(N-c choose r)/(N choose r) for N-c>=r and 1 else with N as total
+      1-(N-c choose r)/(N choose r) for N-c>=r and 1 otherwise, with N
+      as total
       number of hidden service directories, c as compromised nodes, and r as
       number of replicas
 
@@ -595,11 +611,12 @@
     content. The client would detect a false descriptor, because it could not
     contain a correct signature. But an old content or an empty reply could
     confuse the client. Therefore, the countermeasure is to replicate
-    descriptors among a small number of hidden service directories. The
-    probability of a group of collaborating nodes to make a hidden service
+    descriptors among a small number of hidden service directories, e.g. 5.
+    The probability of a group of collaborating nodes to make a hidden service
     completely unavailable is in each period:
 
-      (c choose r)/(N choose r) for c>=r and N>=r, and 0 else with N as total
+      (c choose r)/(N choose r) for c>=r and N>=r, and 0 otherwise,
+      with N as total
       number of hidden service directories, c as compromised nodes, and r as
       number of replicas
 
@@ -937,4 +954,10 @@
     Added rend_decrypt_introduction_points() to decrypt and parse the list of
     introduction points (/14/).
 
- 
\ No newline at end of file
+Test: 
+
+  The changes were tested via test functions in test.c for separate,
+  short-running functionality and using an automatic validation based on
+  PuppeTor.
+
+  
\ No newline at end of file

Modified: tor/branches/114-dist-storage/doc/spec/rend-spec.txt
===================================================================
--- tor/branches/114-dist-storage/doc/spec/rend-spec.txt	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/doc/spec/rend-spec.txt	2007-08-11 22:30:44 UTC (rev 11079)
@@ -138,9 +138,12 @@
 
    The first time the OP provides an advertised service, it generates
    a public/private keypair (stored locally).  Periodically, the OP
-   generates a pair of service descriptors, one "V1" and one "V0".
+   generates and publishes a descriptor of type "V0". The V1 descriptor
+   format in 0.1.1.5-alpha-cvs is understood and accepted, but currently
+   no Tors generate them. The more complex V1 descriptor format below
+   is just speculation and has never been used.
 
-   The "V1" descriptor in 0.1.1.6-alpha contains:
+   A hypothetical "V1" descriptor contains:
 
          V     Format byte: set to 255               [1 octet]
          V     Version byte: set to 1                [1 octet]
@@ -195,17 +198,9 @@
    KL is the length of PK, in octets.
    TS is the number of seconds elapsed since Jan 1, 1970.
 
-   AUTHT specifies which authentication/authorization mechanism is
-   required by the hidden service or the introduction point. AUTHD
-   is arbitrary data that can be associated with an auth approach.
-   Currently only AUTHT of [00 00] is supported, with an AUTHL of 0.
-   See section 2 of this document for details on auth mechanisms.
+   The members of Ipt are identity key digests, encoded in hex, and
+   prefixed with a '$'.
 
-   The members of Ipt may be either (a) nicknames, or (b) identity key
-   digests, encoded in hex, and prefixed with a '$'.  Clients must
-   accept both forms. Services must only generate the second form.
-   Once 0.0.9.x is obsoleted, we can drop the first form.
-
    [It's ok for Bob to advertise 0 introduction points. He might want
     to do that if he previously advertised some introduction points,
     and now he doesn't have any. -RD]
@@ -300,6 +295,16 @@
 
        A bitmask of allowed rendezvous protocols.
 
+     "authentication" ...
+
+
+[   AUTHT specifies which authentication/authorization mechanism is    ]
+[   required by the hidden service or the introduction point. AUTHD    ]
+[   is arbitrary data that can be associated with an auth approach.    ]
+[   Currently only AUTHT of [00 00] is supported, with an AUTHL of 0.  ]
+[   See section 2 of this document for details on auth mechanisms.     ]
+
+
      "introduction-points" NL encrypted-string
 
        [Exactly once]
@@ -344,6 +349,9 @@
            The public key that can be used to encrypt messages to the hidden
            service.
 
+         "authentication" ...
+
+
      "signature" NL signature-string
 
        [At end, exactly once]
@@ -420,7 +428,7 @@
    all hidden service directories. Therefore, Bob's OP opens a stream via Tor
    to all responsible hidden service directories. (He may re-use old circuits
    for this.) Over this stream, Bob's OP makes an HTTP 'POST' request to a URL
-   "/tor/rendezvous/publish2" relative to the hidden service directory's root,
+   "/tor/rendezvous2/publish" relative to the hidden service directory's root,
    containing as its body Bob's service descriptor.
 
    At any time, there are 6 hidden service directories responsible for keeping
@@ -703,7 +711,7 @@
    running for at least 24 hours.) All requests and replies are formatted as
    HTTP messages. Requests are contained within BEGIN_DIR cells, directed to
    the router's directory port, and formatted as HTTP POST requests to the URL
-   "/tor/rendezvous/publish2" relative to the hidden service directory's root,
+   "/tor/rendezvous2/publish" relative to the hidden service directory's root,
    containing as its body a v2 service descriptor.
 
    A hidden service directory node parses every received descriptor and only

Modified: tor/branches/114-dist-storage/src/common/crypto.c
===================================================================
--- tor/branches/114-dist-storage/src/common/crypto.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/common/crypto.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -1203,7 +1203,7 @@
  * <b>key</b> of 16 bytes length to <b>to</b>. The length of <b>to</b> may be
  * the length of <b>from</b> minus 16 (up to 16 bytes for padding and exactly
  * 16 bytes for the initialization vector). On success, return the number of
- * bytes written, on failure, return -1.
+ * bytes written, on failure (including providing the wrong key), return -1.
  */
 int
 crypto_cipher_decrypt_cbc(char *key, char *to, const char *from,
@@ -1233,13 +1233,13 @@
   if (!EVP_DecryptUpdate(&ctx, (unsigned char *)to, &outlen,
                         ((const unsigned char *)from) + 16,
                         (int)fromlen - 16)) {
-    crypto_log_errors(LOG_WARN, "decrypting");
+    crypto_log_errors(LOG_INFO, "decrypting");
     return -1;
   }
 
   /* decrypt the final data */
   if (!EVP_DecryptFinal_ex(&ctx, ((unsigned char *)to) + outlen, &tmplen)) {
-    crypto_log_errors(LOG_WARN, "decrypting the final data");
+    crypto_log_errors(LOG_INFO, "decrypting the final data");
     return -1;
   }
   outlen += tmplen;
@@ -1925,7 +1925,7 @@
   tor_assert((nbits/8) <= destlen); /* We need enough space. */
   tor_assert(destlen < SIZE_T_CEILING);
 
-  /* convert base32 encoded chars to the 5-bit values that they represent */
+  /* Convert base32 encoded chars to the 5-bit values that they represent. */
   tmp = tor_malloc_zero(srclen);
   for (j = 0; j < srclen; ++j) {
     if (src[j] > 0x60 && src[j] < 0x7B) tmp[j] = src[j] - 0x61;
@@ -1936,7 +1936,7 @@
     }
   }
 
-  /* assemble result byte-wise by applying the five possible cases */
+  /* Assemble result byte-wise by applying five possible cases. */
   for (i = 0, bit = 0; bit < nbits; ++i, bit += 8) {
     switch (bit % 40) {
     case 0:

Modified: tor/branches/114-dist-storage/src/or/circuitlist.c
===================================================================
--- tor/branches/114-dist-storage/src/or/circuitlist.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/circuitlist.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -12,9 +12,8 @@
  **/
 
 #include "or.h"
+#include "ht.h"
 
-#include "../common/ht.h"
-
 /********* START VARIABLES **********/
 
 /** A global list of all circuits at this hop. */
@@ -904,22 +903,6 @@
   }
 }
 
-/** Return 1 if there are any origin circuits that use
- * <b>conn</b> as there first hop. Else return 0. */
-static int
-circuit_any_origin_circs_on_conn(or_connection_t *conn)
-{
-  circuit_t *circ;
-
-  for (circ=global_circuitlist; circ; circ = circ->next) {
-    if (CIRCUIT_IS_ORIGIN(circ) &&
-        !circ->marked_for_close &&
-        circ->n_conn == conn)
-      return 1;
-  }
-  return 0;
-}
-
 /** Mark <b>circ</b> to be closed next time we call
  * circuit_close_all_marked(). Do any cleanup needed:
  *   - If state is onionskin_pending, remove circ from the onion_pending
@@ -958,9 +941,9 @@
                file, line, circ->purpose);
     }
     reason = END_CIRC_REASON_NONE;
-  } else if (CIRCUIT_IS_ORIGIN(circ) && reason < _END_CIRC_REASON_MIN) {
-    /* We don't send reasons when closing circuits at the origin, but we want
-     * to track them anyway so we can give them to the controller. */
+  }
+  if (CIRCUIT_IS_ORIGIN(circ)) {
+    /* We don't send reasons when closing circuits at the origin. */
     reason = END_CIRC_REASON_NONE;
   }
 
@@ -1045,12 +1028,7 @@
   circ->marked_for_close = line;
   circ->marked_for_close_file = file;
 
-  if (CIRCUIT_IS_ORIGIN(circ)) {
-    if (circ->n_conn && circ->n_conn->client_used) {
-      circ->n_conn->client_used =
-        circuit_any_origin_circs_on_conn(circ->n_conn);
-    }
-  } else {
+  if (!CIRCUIT_IS_ORIGIN(circ)) {
     or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
     if (or_circ->rend_splice) {
       if (!or_circ->rend_splice->_base.marked_for_close) {

Modified: tor/branches/114-dist-storage/src/or/circuituse.c
===================================================================
--- tor/branches/114-dist-storage/src/or/circuituse.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/circuituse.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -813,8 +813,9 @@
   return circ;
 }
 
-/** Launch a new circuit with purpose <b>purpose</b> and exit node <b>info</b>
- * (or NULL to select a random exit node).  If <b>need_uptime</b> is true,
+/** Launch a new circuit with purpose <b>purpose</b> and exit node
+ * <b>extend_info</b> (or NULL to select a random exit node).
+ * If <b>need_uptime</b> is true,
  * choose among routers with high uptime.  If <b>need_capacity</b> is true,
  * choose among routers with high bandwidth.  If <b>internal</b> is true, the
  * last hop need not be an exit node. Return the newly allocated circuit on
@@ -942,6 +943,7 @@
   int check_exit_policy;
   int need_uptime, need_internal;
   int want_onehop;
+  or_options_t *options = get_options();
 
   tor_assert(conn);
   tor_assert(circp);
@@ -952,7 +954,7 @@
   want_onehop = conn->socks_request->command == SOCKS_COMMAND_CONNECT_DIR;
 
   need_uptime = (conn->socks_request->command == SOCKS_COMMAND_CONNECT) &&
-                smartlist_string_num_isin(get_options()->LongLivedPorts,
+                smartlist_string_num_isin(options->LongLivedPorts,
                                           conn->socks_request->port);
   need_internal = desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL;
 
@@ -966,10 +968,17 @@
 
   if (!want_onehop && !router_have_minimum_dir_info()) {
     if (!connection_get_by_type(CONN_TYPE_DIR)) {
-      log_notice(LD_APP|LD_DIR,
-                 "Application request when we're believed to be "
-                 "offline. Optimistically trying directory fetches again.");
-      routerlist_retry_directory_downloads(time(NULL));
+      if (options->UseBridges && bridges_should_be_retried()) {
+        log_notice(LD_APP|LD_DIR,
+                   "Application request when we're believed to be "
+                   "offline. Optimistically trying known bridges again.");
+        bridges_retry_all();
+      } else if (!options->UseBridges || any_bridge_descriptors_known()) {
+        log_notice(LD_APP|LD_DIR,
+                   "Application request when we're believed to be "
+                   "offline. Optimistically trying directory fetches again.");
+        routerlist_retry_directory_downloads(time(NULL));
+      }
     }
     /* the stream will be dealt with when router_have_minimum_dir_info becomes
      * 1, or when all directory attempts fail and directory_all_unreachable()
@@ -1003,7 +1012,7 @@
 
     if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
       /* need to pick an intro point */
-      extend_info = rend_client_get_random_intro(conn->rend_query);
+      extend_info = rend_client_get_random_intro(conn->rend_query, conn->rend_version);
       if (!extend_info) {
         log_info(LD_REND,
                  "No intro points for '%s': refetching service descriptor.",

Modified: tor/branches/114-dist-storage/src/or/config.c
===================================================================
--- tor/branches/114-dist-storage/src/or/config.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/config.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -17,7 +17,6 @@
 #ifdef MS_WINDOWS
 #include <shlobj.h>
 #endif
-#include "../common/aes.h"
 
 /** Enumeration of types which option values can take */
 typedef enum config_type_t {
@@ -146,6 +145,8 @@
   VAR("CircuitIdleTimeout",  INTERVAL, CircuitIdleTimeout,   "1 hour"),
   VAR("ClientOnly",          BOOL,     ClientOnly,           "0"),
   VAR("ConnLimit",           UINT,     ConnLimit,            "1000"),
+  VAR("ConstrainedSockets",  BOOL,     ConstrainedSockets,   "0"),
+  VAR("ConstrainedSockSize", MEMUNIT,  ConstrainedSockSize,  "8192"),
   VAR("ContactInfo",         STRING,   ContactInfo,          NULL),
   VAR("ControlListenAddress",LINELIST, ControlListenAddress, NULL),
   VAR("ControlPort",         UINT,     ControlPort,          "0"),
@@ -333,6 +334,11 @@
   { "BandwidthBurst", "Limit the maximum token buffer size (also known as "
     "burst) to the given number of bytes." },
   { "ConnLimit", "Maximum number of simultaneous sockets allowed." },
+  { "ConstrainedSockets", "Shrink tx and rx buffers for sockets to avoid "
+    "system limits on vservers and related environments.  See man page for "
+    "more information regarding this option." },
+  { "ConstrainedSockSize", "Limit socket buffers to this size when "
+    "ConstrainedSockets is enabled." },
   /*  ControlListenAddress */
   { "ControlPort", "If set, Tor will accept connections from the same machine "
     "(localhost only) on this port, and allow those connections to control "
@@ -711,21 +717,22 @@
 
 extern const char tor_svn_revision[]; /* from tor_main.c */
 
+static char *_version = NULL;
+
 /** Return the current Tor version, possibly */
 const char *
 get_version(void)
 {
-  static char *version = NULL;
-  if (version == NULL) {
+  if (_version == NULL) {
     if (strlen(tor_svn_revision)) {
       size_t len = strlen(VERSION)+strlen(tor_svn_revision)+8;
-      version = tor_malloc(len);
-      tor_snprintf(version, len, "%s (r%s)", VERSION, tor_svn_revision);
+      _version = tor_malloc(len);
+      tor_snprintf(_version, len, "%s (r%s)", VERSION, tor_svn_revision);
     } else {
-      version = tor_strdup(VERSION);
+      _version = tor_strdup(VERSION);
     }
   }
-  return version;
+  return _version;
 }
 
 /** Release all memory and resources held by global configuration structures.
@@ -742,6 +749,7 @@
     global_state = NULL;
   }
   tor_free(torrc_fname);
+  tor_free(_version);
 }
 
 /** If options->SafeLogging is on, return a not very useful string,
@@ -750,6 +758,10 @@
 const char *
 safe_str(const char *address)
 {
+  if (!address) { /* XXX020 eventually turn this into an assert */
+    log_warn(LD_BUG, "safe_str() called with NULL address.");
+    return "EMPTY";
+  }
   if (get_options()->SafeLogging)
     return "[scrubbed]";
   else
@@ -2373,6 +2385,8 @@
 {
   tor_assert(auth);
   *auth = NO_AUTHORITY;
+  if (!list) /* empty list, answer is none */
+    return 0;
   SMARTLIST_FOREACH(list, const char *, string, {
     if (!strcasecmp(string, "v1"))
       *auth |= V1_AUTHORITY;
@@ -2448,7 +2462,7 @@
     REJECT("DirPort must be defined if DirListenAddress is defined.");
 
   if (options->DNSPort == 0 && options->DNSListenAddress != NULL)
-    REJECT("DirPort must be defined if DirListenAddress is defined.");
+    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.");
@@ -2622,7 +2636,7 @@
                "extra-info documents. Setting DownloadExtraInfo.");
       options->DownloadExtraInfo = 1;
     }
-    /* XXXX020 Check that at least one of Bridge/HS/V1/V2/V2{AoritativeDir}
+    /* XXXX020 Check that at least one of Bridge/HS/V1/V2/V2{AuthoritativeDir}
      * is set. */
   }
 
@@ -2872,6 +2886,37 @@
   if (options->HashedControlPassword && options->CookieAuthentication)
     REJECT("Cannot set both HashedControlPassword and CookieAuthentication");
 
+  if (options->ControlListenAddress) {
+    int all_are_local = 1;
+    config_line_t *ln;
+    for (ln = options->ControlListenAddress; ln; ln = ln->next) {
+      if (strcmpstart(ln->value, "127."))
+        all_are_local = 0;
+    }
+    if (!all_are_local) {
+      if (!options->HashedControlPassword && !options->CookieAuthentication) {
+        log_warn(LD_CONFIG, "You have a ControlListenAddress set to accept "
+                 "connections from a non-local address.  This means that "
+                 "any program on the internet can reconfigure your Tor. "
+                 "That's so bad that I'm closing your ControlPort for you.");
+        options->ControlPort = 0;
+      } else {
+        log_warn(LD_CONFIG, "You have a ControlListenAddress set to accept "
+                 "connections from a non-local address.  This means that "
+                 "programs not running on your computer can reconfigure your "
+                 "Tor.  That's pretty bad!");
+      }
+    }
+  }
+
+  if (options->ControlPort && !options->HashedControlPassword &&
+      !options->CookieAuthentication) {
+    log_warn(LD_CONFIG, "ControlPort is open, but no authentication method "
+             "has been configured.  This means that any program on your "
+             "computer can reconfigure your Tor.  That's bad!  You should "
+             "upgrade your Tor controller as soon as possible.");
+  }
+
   if (options->UseEntryGuards && ! options->NumEntryGuards)
     REJECT("Cannot enable UseEntryGuards with NumEntryGuards set to 0");
 
@@ -2928,6 +2973,29 @@
     }
   }
 
+  if (options->ConstrainedSockets) {
+    /* If the user wants to constrain socket buffer use, make sure the desired
+     * limit is between MIN|MAX_TCPSOCK_BUFFER in k increments. */
+    if (options->ConstrainedSockSize < MIN_CONSTRAINED_TCP_BUFFER ||
+        options->ConstrainedSockSize > MAX_CONSTRAINED_TCP_BUFFER ||
+        options->ConstrainedSockSize % 1024) {
+      r = tor_snprintf(buf, sizeof(buf),
+          "ConstrainedSockSize is invalid.  Must be a value between %d and %d "
+          "in 1024 byte increments.",
+          MIN_CONSTRAINED_TCP_BUFFER, MAX_CONSTRAINED_TCP_BUFFER);
+      *msg = tor_strdup(r >= 0 ? buf : "internal error");
+      return -1;
+    }
+    if (options->DirPort) {
+      /* Providing cached directory entries while system TCP buffers are scarce
+       * will exacerbate the socket errors.  Suggest that this be disabled. */
+      COMPLAIN("You have requested constrained socket buffers while also "
+               "serving directory entries via DirPort.  It is strongly "
+               "suggested that you disable serving directory requests when "
+               "system TCP buffer resources are scarce.");
+    }
+  }
+
   if (rend_config_services(options, 1) < 0)
     REJECT("Failed to configure rendezvous options. See logs for details.");
 
@@ -3533,8 +3601,8 @@
     *msg = tor_strdup("Wrong number of elements in RedirectExit line");
     goto err;
   }
-  if (parse_addr_and_port_range(smartlist_get(elements,0),&r->addr,&r->mask,
-                                &r->port_min,&r->port_max)) {
+  if (parse_addr_and_port_range(smartlist_get(elements,0),&r->addr,
+                                &r->maskbits,&r->port_min,&r->port_max)) {
     *msg = tor_strdup("Error parsing source address in RedirectExit line");
     goto err;
   }
@@ -4490,8 +4558,9 @@
   return 0;
 }
 
-#include "../common/ht.h"
-#include "../common/test.h"
+#include "aes.h"
+#include "ht.h"
+#include "test.h"
 
 extern const char aes_c_id[];
 extern const char compat_c_id[];

Modified: tor/branches/114-dist-storage/src/or/connection_edge.c
===================================================================
--- tor/branches/114-dist-storage/src/or/connection_edge.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/connection_edge.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -59,7 +59,7 @@
     else if (SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command))
       connection_ap_handshake_socks_resolved(conn,
                                              RESOLVED_TYPE_ERROR_TRANSIENT,
-                                             0, NULL, -1);
+                                             0, NULL, -1, -1);
     else /* unknown or no handshake at all. send no response. */
       conn->socks_request->has_finished = 1;
   }
@@ -617,14 +617,14 @@
 void
 addressmap_clear_configured(void)
 {
-  addressmap_get_mappings(NULL, 0, 0);
+  addressmap_get_mappings(NULL, 0, 0, 0);
 }
 
 /** Remove all entries from the addressmap that are set to expire, ever. */
 void
 addressmap_clear_transient(void)
 {
-  addressmap_get_mappings(NULL, 2, TIME_MAX);
+  addressmap_get_mappings(NULL, 2, TIME_MAX, 0);
 }
 
 /** Clean out entries from the addressmap cache that were
@@ -633,7 +633,7 @@
 void
 addressmap_clean(time_t now)
 {
-  addressmap_get_mappings(NULL, 2, now);
+  addressmap_get_mappings(NULL, 2, now, 0);
 }
 
 /** Free all the elements in the addressmap, and free the addressmap
@@ -654,24 +654,32 @@
 /** Look at address, and rewrite it until it doesn't want any
  * more rewrites; but don't get into an infinite loop.
  * Don't write more than maxlen chars into address.  Return true if the
- * address changed; false otherwise.
+ * address changed; false otherwise.  Set *<b>expires_out</b> to the
+ * expiry time of the result, or to <b>time_max</b> if the result does
+ * not expire.
  */
 int
-addressmap_rewrite(char *address, size_t maxlen)
+addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out)
 {
   addressmap_entry_t *ent;
   int rewrites;
   char *cp;
+  time_t expires = TIME_MAX;
 
   for (rewrites = 0; rewrites < 16; rewrites++) {
     ent = strmap_get(addressmap, address);
 
-    if (!ent || !ent->new_address)
+    if (!ent || !ent->new_address) {
+      if (expires_out)
+        *expires_out = expires;
       return (rewrites > 0); /* done, no rewrite needed */
+    }
 
     cp = tor_strdup(escaped_safe_str(ent->new_address));
     log_info(LD_APP, "Addressmap: rewriting %s to %s",
              escaped_safe_str(address), cp);
+    if (ent->expires > 1 && ent->expires < expires)
+      expires = ent->expires;
     tor_free(cp);
     strlcpy(address, ent->new_address, maxlen);
   }
@@ -679,14 +687,18 @@
            "Loop detected: we've rewritten %s 16 times! Using it as-is.",
            escaped_safe_str(address));
   /* it's fine to rewrite a rewrite, but don't loop forever */
+  if (expires_out)
+    *expires_out = TIME_MAX;
   return 1;
 }
 
 /** If we have a cached reverse DNS entry for the address stored in the
  * <b>maxlen</b>-byte buffer <b>address</b> (typically, a dotted quad) then
- * rewrite to the cached value and return 1.  Otherwise return 0. */
+ * rewrite to the cached value and return 1.  Otherwise return 0.  Set
+ * *<b>expires_out</b> to the expiry time of the result, or to <b>time_max</b>
+ * if the result does not expire. */
 static int
-addressmap_rewrite_reverse(char *address, size_t maxlen)
+addressmap_rewrite_reverse(char *address, size_t maxlen, time_t *expires_out)
 {
   size_t len = maxlen + 16;
   char *s = tor_malloc(len), *cp;
@@ -702,6 +714,10 @@
     strlcpy(address, ent->new_address, maxlen);
     r = 1;
   }
+
+  if (expires_out)
+    *expires_out = (ent && ent->expires > 1) ? ent->expires : TIME_MAX;
+
   tor_free(s);
   return r;
 }
@@ -765,7 +781,7 @@
 
   log_info(LD_CONFIG, "Addressmap: (re)mapped '%s' to '%s'",
            safe_str(address), safe_str(ent->new_address));
-  control_event_address_mapped(address, ent->new_address, expires);
+  control_event_address_mapped(address, ent->new_address, expires, NULL);
 }
 
 /** An attempt to resolve <b>address</b> failed at some OR.
@@ -903,8 +919,7 @@
  * These options are configured by parse_virtual_addr_network().
  */
 static uint32_t virtual_addr_network = 0x7fc00000u;
-static uint32_t virtual_addr_netmask = 0xffc00000u;
-static int virtual_addr_netmask_bits = 10;
+static maskbits_t virtual_addr_netmask_bits = 10;
 static uint32_t next_virtual_addr    = 0x7fc00000u;
 
 /** Read a netmask of the form 127.192.0.0/10 from "val", and check whether
@@ -916,11 +931,11 @@
 parse_virtual_addr_network(const char *val, int validate_only,
                            char **msg)
 {
-  uint32_t addr, mask;
+  uint32_t addr;
   uint16_t port_min, port_max;
-  int bits;
+  maskbits_t bits;
 
-  if (parse_addr_and_port_range(val, &addr, &mask, &port_min, &port_max)) {
+  if (parse_addr_and_port_range(val, &addr, &bits, &port_min, &port_max)) {
     if (msg) *msg = tor_strdup("Error parsing VirtualAddressNetwork");
     return -1;
   }
@@ -930,13 +945,6 @@
     return -1;
   }
 
-  bits = addr_mask_get_bits(mask);
-  if (bits < 0) {
-    if (msg) *msg = tor_strdup("VirtualAddressNetwork must have a mask that "
-                               "can be expressed as a prefix");
-    return -1;
-  }
-
   if (bits > 16) {
     if (msg) *msg = tor_strdup("VirtualAddressNetwork expects a /16 "
                                "network or larger");
@@ -946,11 +954,10 @@
   if (validate_only)
     return 0;
 
-  virtual_addr_network = addr & mask;
-  virtual_addr_netmask = mask;
+  virtual_addr_network = addr & (0xfffffffful << (32-bits));
   virtual_addr_netmask_bits = bits;
 
-  if ((next_virtual_addr & mask) != addr)
+  if (addr_mask_cmp_bits(next_virtual_addr, addr, bits))
     next_virtual_addr = addr;
 
   return 0;
@@ -969,7 +976,8 @@
     return 1;
   } else if (tor_inet_aton(address, &in)) {
     uint32_t addr = ntohl(in.s_addr);
-    if ((addr & virtual_addr_netmask) == virtual_addr_network)
+    if (!addr_mask_cmp_bits(addr, virtual_addr_network,
+                            virtual_addr_netmask_bits))
       return 1;
   }
   return 0;
@@ -1015,7 +1023,8 @@
         log_warn(LD_CONFIG, "Ran out of virtual addresses!");
         return NULL;
       }
-      if ((next_virtual_addr & virtual_addr_netmask) != virtual_addr_network)
+      if (!addr_mask_cmp_bits(next_virtual_addr, virtual_addr_network,
+                              virtual_addr_netmask_bits))
         next_virtual_addr = virtual_addr_network;
     }
     return tor_strdup(buf);
@@ -1122,12 +1131,13 @@
 
 /** Iterate over all address mappings which have expiry times between
  * min_expires and max_expires, inclusive.  If sl is provided, add an
- * "old-addr new-addr" string to sl for each mapping.  If sl is NULL,
- * remove the mappings.
+ * "old-addr new-addr expiry" string to sl for each mapping, omitting
+ * the expiry time if want_expiry is false. If sl is NULL, remove the
+ * mappings.
  */
 void
 addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
-                        time_t max_expires)
+                        time_t max_expires, int want_expiry)
 {
    strmap_iter_t *iter;
    const char *key;
@@ -1146,9 +1156,20 @@
          addressmap_ent_remove(key, val);
          continue;
        } else if (val->new_address) {
-         size_t len = strlen(key)+strlen(val->new_address)+2;
+         size_t len = strlen(key)+strlen(val->new_address)+ISO_TIME_LEN+5;
          char *line = tor_malloc(len);
-         tor_snprintf(line, len, "%s %s", key, val->new_address);
+         if (want_expiry) {
+           if (val->expires < 3 || val->expires == TIME_MAX)
+             tor_snprintf(line, len, "%s %s NEVER", key, val->new_address);
+           else {
+             char time[ISO_TIME_LEN+1];
+             format_iso_time(time, val->expires);
+             tor_snprintf(line, len, "%s %s \"%s\"", key, val->new_address,
+                          time);
+           }
+         } else {
+           tor_snprintf(line, len, "%s %s", key, val->new_address);
+         }
          smartlist_add(sl, line);
        }
      }
@@ -1182,6 +1203,7 @@
   struct in_addr addr_tmp;
   int automap = 0;
   char orig_address[MAX_SOCKS_ADDR_LEN];
+  time_t map_expires = TIME_MAX;
 
   tor_strlower(socks->address); /* normalize it */
   strlcpy(orig_address, socks->address, sizeof(orig_address));
@@ -1209,12 +1231,14 @@
   }
 
   if (socks->command == SOCKS_COMMAND_RESOLVE_PTR) {
-    if (addressmap_rewrite_reverse(socks->address, sizeof(socks->address))) {
+    if (addressmap_rewrite_reverse(socks->address, sizeof(socks->address),
+                                   &map_expires)) {
       char *result = tor_strdup(socks->address);
       /* remember _what_ is supposed to have been resolved. */
       strlcpy(socks->address, orig_address, sizeof(socks->address));
       connection_ap_handshake_socks_resolved(conn, RESOLVED_TYPE_HOSTNAME,
-                                             strlen(result), result, -1);
+                                             strlen(result), result, -1,
+                                             map_expires);
       connection_mark_unattached_ap(conn,
                                  END_STREAM_REASON_DONE |
                                  END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
@@ -1222,7 +1246,8 @@
     }
   } else if (!automap) {
     /* For address map controls, remap the address. */
-    if (addressmap_rewrite(socks->address, sizeof(socks->address))) {
+    if (addressmap_rewrite(socks->address, sizeof(socks->address),
+                           &map_expires)) {
       control_event_stream_status(conn, STREAM_EVENT_REMAP,
                                   REMAP_STREAM_SOURCE_CACHE);
     }
@@ -1309,7 +1334,7 @@
                                     escaped(socks->address));
         connection_ap_handshake_socks_resolved(conn,
                                                RESOLVED_TYPE_ERROR_TRANSIENT,
-                                               0,NULL,-1);
+                                               0,NULL,-1,TIME_MAX);
         connection_mark_unattached_ap(conn,
                                 END_STREAM_REASON_SOCKSPROTOCOL |
                                 END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
@@ -1321,7 +1346,7 @@
         /* remember _what_ is supposed to have been resolved. */
         strlcpy(socks->address, orig_address, sizeof(socks->address));
         connection_ap_handshake_socks_resolved(conn,RESOLVED_TYPE_IPV4,4,
-                                               (char*)&answer,-1);
+                                               (char*)&answer,-1,map_expires);
         connection_mark_unattached_ap(conn,
                                 END_STREAM_REASON_DONE |
                                 END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
@@ -1383,7 +1408,7 @@
       log_warn(LD_APP,
                "Resolve requests to hidden services not allowed. Failing.");
       connection_ap_handshake_socks_resolved(conn,RESOLVED_TYPE_ERROR,
-                                             0,NULL,-1);
+                                             0,NULL,-1,TIME_MAX);
       connection_mark_unattached_ap(conn,
                                 END_STREAM_REASON_SOCKSPROTOCOL |
                                 END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
@@ -1401,20 +1426,22 @@
     log_info(LD_REND,"Got a hidden service request for ID '%s'",
              safe_str(conn->rend_query));
     /* see if we already have it cached */
-    r = rend_cache_lookup_entry(conn->rend_query, -1, &entry);
+    if (rend_valid_service_id(socks->address) == 2) {
+      r = rend_cache_lookup_entry(conn->rend_query, 2, &entry);
+      conn->rend_version = 2;
+      strlcpy(conn->secret_cookie, socks->address + 17,
+              sizeof(conn->secret_cookie));
+      log_info(LD_REND, "Got a hidden service request for a v2 service.");
+    } else {
+      r = rend_cache_lookup_entry(conn->rend_query, 0, &entry);
+      conn->rend_version = 0;
+    }
     if (r<0) {
       log_warn(LD_BUG,"Invalid service name '%s'",
                safe_str(conn->rend_query));
       connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
       return -1;
     }
-    if (rend_valid_service_id(socks->address) == 2) {
-      conn->rend_version = 2;
-      strlcpy(conn->secret_cookie, socks->address+17,
-              sizeof(conn->secret_cookie));
-    } else {
-      conn->rend_version = 1;
-    }
     if (r==0) {
       conn->_base.state = AP_CONN_STATE_RENDDESC_WAIT;
       log_info(LD_REND, "Unknown descriptor %s. Fetching.",
@@ -1903,7 +1930,7 @@
     tor_assert(payload_len <= RELAY_PAYLOAD_SIZE);
   }
 
-  log_debug(LD_APP,
+  log_info(LD_APP,
             "Sending relay cell to begin stream %d.", ap_conn->stream_id);
 
   if (connection_edge_send_command(ap_conn,
@@ -1954,7 +1981,7 @@
                   digest, DIGEST_LEN);
   }
 
-  conn->_base.address = tor_strdup("(local link)");
+  conn->_base.address = tor_strdup("(Tor_internal)");
   conn->_base.addr = 0;
   conn->_base.port = 0;
 
@@ -1977,17 +2004,52 @@
   return conn;
 }
 
+/** DOCDOC */
+static void
+tell_controller_about_resolved_result(edge_connection_t *conn,
+                                      int answer_type,
+                                      size_t answer_len,
+                                      const char *answer,
+                                      int ttl,
+                                      time_t expires)
+{
+
+  if (ttl >= 0 && (answer_type == RESOLVED_TYPE_IPV4 ||
+                   answer_type == RESOLVED_TYPE_HOSTNAME)) {
+    return; /* we already told the controller. */
+  } else if (answer_type == RESOLVED_TYPE_IPV4 && answer_len >= 4) {
+    struct in_addr in;
+    char buf[INET_NTOA_BUF_LEN];
+    in.s_addr = get_uint32(answer);
+    tor_inet_ntoa(&in, buf, sizeof(buf));
+    control_event_address_mapped(conn->socks_request->address,
+                                 buf, expires, NULL);
+  } else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len <256) {
+    char *cp = tor_strndup(answer, answer_len);
+    control_event_address_mapped(conn->socks_request->address,
+                                 cp, expires, NULL);
+    tor_free(cp);
+  } else {
+    control_event_address_mapped(conn->socks_request->address,
+                                 "<error>",
+                                 time(NULL)+ttl,
+                                 "error=yes");
+  }
+}
+
 /** Send an answer to an AP connection that has requested a DNS lookup
  * via SOCKS.  The type should be one of RESOLVED_TYPE_(IPV4|IPV6|HOSTNAME) or
  * -1 for unreachable; the answer should be in the format specified
  * in the socks extensions document.
+ * DOCDOC expires
  **/
 void
 connection_ap_handshake_socks_resolved(edge_connection_t *conn,
                                        int answer_type,
                                        size_t answer_len,
                                        const char *answer,
-                                       int ttl)
+                                       int ttl,
+                                       time_t expires)
 {
   char buf[384];
   size_t replylen;
@@ -2007,11 +2069,21 @@
     }
   }
 
-  if (conn->dns_server_request) {
-    /* We had a request on our DNS port: answer it. */
-    dnsserv_resolved(conn, answer_type, answer_len, answer, ttl);
-    conn->socks_request->has_finished = 1;
-    return;
+  if (conn->is_dns_request) {
+    if (conn->dns_server_request) {
+      /* We had a request on our DNS port: answer it. */
+      dnsserv_resolved(conn, answer_type, answer_len, answer, ttl);
+      conn->socks_request->has_finished = 1;
+      return;
+    } else {
+      /* This must be a request from the controller. We already sent
+       * a mapaddress if there's a ttl. */
+      tell_controller_about_resolved_result(conn, answer_type, answer_len,
+                                            answer, ttl, expires);
+      conn->socks_request->has_finished = 1;
+      return;
+    }
+    /* XXXX020 are we freeing conn anywhere? */
   }
 
   if (conn->socks_request->socks_version == 4) {
@@ -2242,7 +2314,7 @@
 
   if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) {
     origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
-    log_debug(LD_REND,"begin is for rendezvous. configuring stream.");
+    log_info(LD_REND,"begin is for rendezvous. configuring stream.");
     n_stream->_base.address = tor_strdup("(rendezvous)");
     n_stream->_base.state = EXIT_CONN_STATE_CONNECTING;
     strlcpy(n_stream->rend_query, origin_circ->rend_query,
@@ -2392,7 +2464,7 @@
   if (redirect_exit_list) {
     SMARTLIST_FOREACH(redirect_exit_list, exit_redirect_t *, r,
     {
-      if ((addr&r->mask)==(r->addr&r->mask) &&
+      if (!addr_mask_cmp_bits(addr, r->addr, r->maskbits) &&
           (r->port_min <= port) && (port <= r->port_max)) {
         struct in_addr in;
         if (r->is_redirect) {
@@ -2604,7 +2676,6 @@
 parse_extended_hostname(char *address)
 {
     char *s;
-
     s = strrchr(address,'.');
     if (!s)
       return NORMAL_HOSTNAME; /* no dot, thus normal */
@@ -2617,7 +2688,7 @@
 
     /* so it is .onion */
     *s = 0; /* nul-terminate it */
-    if (rend_valid_service_id(address))
+    if (rend_valid_service_id(address) >= 0)
       return ONION_HOSTNAME; /* success */
 
     /* otherwise, return to previous state and return 0 */

Modified: tor/branches/114-dist-storage/src/or/directory.c
===================================================================
--- tor/branches/114-dist-storage/src/or/directory.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/directory.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -30,13 +30,13 @@
  * - connection_dir_finished_connecting(), called from
  *   connection_finished_connecting() in connection.c
  */
-static void
-directory_send_command(dir_connection_t *conn,
+static void directory_send_command(dir_connection_t *conn,
                        int purpose, int direct, const char *resource,
                        const char *payload, size_t payload_len);
 static int directory_handle_command(dir_connection_t *conn);
 static int body_is_plausible(const char *body, size_t body_len, int purpose);
-static int purpose_needs_anonymity(uint8_t purpose);
+static int purpose_needs_anonymity(uint8_t dir_purpose,
+                                   uint8_t router_purpose);
 static char *http_get_header(const char *headers, const char *which);
 static void http_set_address_origin(const char *headers, connection_t *conn);
 static void connection_dir_download_networkstatus_failed(
@@ -71,16 +71,20 @@
 /** Return true iff the directory purpose 'purpose' must use an
  * anonymous connection to a directory. */
 static int
-purpose_needs_anonymity(uint8_t purpose)
+purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose)
 {
   if (get_options()->AllDirActionsPrivate)
     return 1;
-  if (purpose == DIR_PURPOSE_FETCH_DIR ||
-      purpose == DIR_PURPOSE_UPLOAD_DIR ||
-      purpose == DIR_PURPOSE_FETCH_RUNNING_LIST ||
-      purpose == DIR_PURPOSE_FETCH_NETWORKSTATUS ||
-      purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
-      purpose == DIR_PURPOSE_FETCH_EXTRAINFO)
+  if (router_purpose == ROUTER_PURPOSE_BRIDGE)
+    return 1; /* if we have to ask, better make it anonymous */
+  if (dir_purpose == DIR_PURPOSE_FETCH_DIR ||
+      dir_purpose == DIR_PURPOSE_UPLOAD_DIR ||
+      dir_purpose == DIR_PURPOSE_UPLOAD_VOTE ||
+      dir_purpose == DIR_PURPOSE_UPLOAD_SIGNATURES ||
+      dir_purpose == DIR_PURPOSE_FETCH_RUNNING_LIST ||
+      dir_purpose == DIR_PURPOSE_FETCH_NETWORKSTATUS ||
+      dir_purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
+      dir_purpose == DIR_PURPOSE_FETCH_EXTRAINFO)
     return 0;
   return 1;
 }
@@ -147,7 +151,8 @@
  * support it.
  */
 void
-directory_post_to_dirservers(uint8_t purpose, authority_type_t type,
+directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
+                             authority_type_t type,
                              const char *payload,
                              size_t payload_len, size_t extrainfo_len)
 {
@@ -167,7 +172,7 @@
         continue;
 
       found = 1; /* at least one authority of this type was listed */
-      if (purpose == DIR_PURPOSE_UPLOAD_DIR)
+      if (dir_purpose == DIR_PURPOSE_UPLOAD_DIR)
         ds->has_accepted_serverdesc = 0;
 
       if (extrainfo_len && router_supports_extrainfo(ds->digest, 1)) {
@@ -175,10 +180,10 @@
         log_info(LD_DIR, "Uploading an extrainfo (length %d)",
                  (int) extrainfo_len);
       }
-      post_via_tor = purpose_needs_anonymity(purpose) ||
+      post_via_tor = purpose_needs_anonymity(dir_purpose, router_purpose) ||
               !fascist_firewall_allows_address_dir(ds->addr, ds->dir_port);
-      directory_initiate_command_routerstatus(rs, purpose,
-                                              ROUTER_PURPOSE_GENERAL,
+      directory_initiate_command_routerstatus(rs, dir_purpose,
+                                              router_purpose,
                                               post_via_tor,
                                               NULL, payload, upload_len);
     });
@@ -190,79 +195,34 @@
   }
 }
 
-/** Start a connection to (1 or more?) hidden service directories, uploading
- * the payload 'payload' (length 'payload_len').
- *
- * desc_id = 32 base32 chars, desc = NUL-terminated ASCII-based v2 descriptor
- *
- * TODO114 enable tunneling when available!!
- */
-void
-directory_post_to_hs_dir(const char *desc_id, const char *desc)
-{
-  tor_assert(desc_id);
-  tor_assert(strlen(desc_id) == 32);
-  tor_assert(desc);
-  routerstatus_t *hs_dir = get_responsible_hs_dir(desc_id);
-  directory_initiate_command_routerstatus(hs_dir,
-                                          DIR_PURPOSE_UPLOAD_RENDDESC_V2,
-                                          ROUTER_PURPOSE_GENERAL, 0,
-                                          NULL, desc, strlen(desc));
-}
-
-/** Start a connection to (1 or more?) hidden service directories, downloading
- * the payload 'payload' (length 'payload_len').
- *
- * desc_id = 32 base32 chars, query = 16 base32 chars,
- * secret_cookie = 24 base32 chars
- *
- * TODO114 enable tunneling when available!!
- */
-void
-directory_get_from_hs_dir(const char *desc_id, const char *query,
-                          const char *secret_cookie)
-{
-  tor_assert(desc_id);
-  tor_assert(strlen(desc_id) == 32);
-  tor_assert(query);
-  tor_assert(strlen(query) == 16);
-  tor_assert(secret_cookie);
-  tor_assert(strlen(secret_cookie) == 24);
-  routerstatus_t *hs_dir = get_responsible_hs_dir(desc_id);
-  char orig_request[16+1+24+1];
-  tor_snprintf(orig_request, sizeof(orig_request), "%s.%s", query,
-               secret_cookie);
-  directory_initiate_command_routerstatus(hs_dir,
-                                          DIR_PURPOSE_FETCH_RENDDESC_V2,
-                                          ROUTER_PURPOSE_GENERAL, 0,
-                                          desc_id, orig_request, 0);
-  return;
-}
-
 /** Start a connection to a random running directory server, using
- * connection purpose 'purpose' and requesting 'resource'.
+ * connection purpose <b>dir_purpose</b>, intending to fetch descriptors
+ * of purpose <b>router_purpose</b>, and requesting <b>resource</b>.
  * If <b>retry_if_no_servers</b>, then if all the possible servers seem
  * down, mark them up and try again.
  */
 void
-directory_get_from_dirserver(uint8_t dir_purpose, const char *resource,
-                             int retry_if_no_servers)
+directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
+                             const char *resource, int retry_if_no_servers)
 {
   routerstatus_t *rs = NULL;
   or_options_t *options = get_options();
   int prefer_authority = server_mode(options) && options->DirPort != 0;
-  int directconn = !purpose_needs_anonymity(dir_purpose);
+  int get_via_tor = purpose_needs_anonymity(dir_purpose, router_purpose);
   authority_type_t type;
 
   /* FFFF we could break this switch into its own function, and call
    * it elsewhere in directory.c. -RD */
   switch (dir_purpose) {
     case DIR_PURPOSE_FETCH_EXTRAINFO:
-      type = EXTRAINFO_CACHE | V2_AUTHORITY;
+      type = EXTRAINFO_CACHE |
+             (router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_AUTHORITY :
+                                                        V2_AUTHORITY);
       break;
     case DIR_PURPOSE_FETCH_NETWORKSTATUS:
     case DIR_PURPOSE_FETCH_SERVERDESC:
-      type = V2_AUTHORITY;
+      type = (router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_AUTHORITY :
+                                                        V2_AUTHORITY);
       break;
     case DIR_PURPOSE_FETCH_DIR:
     case DIR_PURPOSE_FETCH_RUNNING_LIST:
@@ -279,21 +239,21 @@
   if (!options->FetchServerDescriptors && type != HIDSERV_AUTHORITY)
     return;
 
-  if (directconn && options->UseBridges) {
-    /* want to pick a bridge for which we have a descriptor. */
+  if (!get_via_tor && options->UseBridges) {
+    /* want to ask a running bridge for which we have a descriptor. */
     routerinfo_t *ri = choose_random_entry(NULL);
     if (ri) {
       directory_initiate_command(ri->address, ri->addr,
                                  ri->or_port, 0,
                                  1, ri->cache_info.identity_digest,
                                  dir_purpose,
-                                 ROUTER_PURPOSE_GENERAL,
+                                 router_purpose,
                                  0, resource, NULL, 0);
     } else
       log_notice(LD_DIR, "Ignoring directory request, since no bridge "
                          "nodes are available yet.");
     return;
-  } else if (directconn) {
+  } else if (!get_via_tor) {
     if (prefer_authority) {
       /* only ask authdirservers, and don't ask myself */
       rs = router_pick_trusteddirserver(type, 1, 1,
@@ -319,10 +279,10 @@
         rs = router_pick_trusteddirserver(type, 1, 1,
                                           retry_if_no_servers);
         if (!rs)
-          directconn = 0; /* last resort: try routing it via Tor */
+          get_via_tor = 1; /* last resort: try routing it via Tor */
       }
     }
-  } else { /* !directconn */
+  } else { /* get_via_tor */
     /* Never use fascistfirewall; we're going via Tor. */
     if (dir_purpose == DIR_PURPOSE_FETCH_RENDDESC) {
       /* only ask hidserv authorities, any of them will do */
@@ -340,15 +300,15 @@
 
   if (rs)
     directory_initiate_command_routerstatus(rs, dir_purpose,
-                                            ROUTER_PURPOSE_GENERAL,
-                                            !directconn,
+                                            router_purpose,
+                                            get_via_tor,
                                             resource, NULL, 0);
   else {
     log_notice(LD_DIR,
                "While fetching directory info, "
                "no running dirservers known. Will try again later. "
                "(purpose %d)", dir_purpose);
-    if (!purpose_needs_anonymity(dir_purpose)) {
+    if (!purpose_needs_anonymity(dir_purpose, router_purpose)) {
       /* remember we tried them all and failed. */
       directory_all_unreachable(time(NULL));
     }
@@ -359,7 +319,8 @@
  * upload or download a server or rendezvous
  * descriptor. <b>dir_purpose</b> determines what
  * kind of directory connection we're launching, and must be one of
- * DIR_PURPOSE_{FETCH|UPLOAD}_{DIR|RENDDESC|RENDDESC_V2}.
+ * DIR_PURPOSE_{FETCH|UPLOAD}_{DIR|RENDDESC|RENDDESC_V2} or
+ * DIR_PURPOSE_REPLICATE_RENDDESC_V2.
  * <b>router_purpose</b> specifies the descriptor purposes we have in mind
  * (currently only used for FETCH_DIR).
  *
@@ -382,6 +343,7 @@
   char address_buf[INET_NTOA_BUF_LEN+1];
   struct in_addr in;
   const char *address;
+log_warn(LD_REND, "directory_initiate_command_routerstatus 1");
   if ((router = router_get_by_digest(status->identity_digest))) {
     address = router->address;
   } else {
@@ -389,6 +351,7 @@
     tor_inet_ntoa(&in, address_buf, sizeof(address_buf));
     address = address_buf;
   }
+log_warn(LD_REND, "directory_initiate_command_routerstatus 2");
   directory_initiate_command(address, status->addr,
                              status->or_port, status->dir_port,
                              status->version_supports_begindir,
@@ -436,8 +399,8 @@
       conn->_base.purpose == DIR_PURPOSE_FETCH_RUNNING_LIST) {
     log_info(LD_DIR, "Giving up on directory server at '%s:%d'; retrying",
              conn->_base.address, conn->_base.port);
-    directory_get_from_dirserver(conn->_base.purpose, NULL,
-                                 0 /* don't retry_if_no_servers */);
+    directory_get_from_dirserver(conn->_base.purpose, conn->router_purpose,
+                                 NULL, 0 /* don't retry_if_no_servers */);
   } else if (conn->_base.purpose == DIR_PURPOSE_FETCH_NETWORKSTATUS) {
     log_info(LD_DIR, "Giving up on directory server at '%s'; retrying",
              conn->_base.address);
@@ -471,8 +434,8 @@
     smartlist_t *trusted_dirs = router_get_trusted_dir_servers();
     SMARTLIST_FOREACH(trusted_dirs, trusted_dir_server_t *, ds,
                       ++ds->n_networkstatus_failures);
-    directory_get_from_dirserver(conn->_base.purpose, "all.z",
-                                 0 /* don't retry_if_no_servers */);
+    directory_get_from_dirserver(conn->_base.purpose, conn->router_purpose,
+                                 "all.z", 0 /* don't retry_if_no_servers */);
   } else if (!strcmpstart(conn->requested_resource, "fp/")) {
     /* We were trying to download by fingerprint; mark them all as having
      * failed, and possibly retry them later.*/
@@ -522,6 +485,7 @@
   int want_to_tunnel = options->TunnelDirConns && supports_begindir &&
                        !anonymized_connection && or_port &&
                        fascist_firewall_allows_address_or(addr, or_port);
+log_warn(LD_REND, "directory_initiate_command 1");
 
   tor_assert(address);
   tor_assert(addr);
@@ -550,6 +514,15 @@
     case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
       log_debug(LD_REND,"initiating hidden-service v2 descriptor upload");
       break;
+    case DIR_PURPOSE_REPLICATE_RENDDESC_V2:
+      log_debug(LD_REND,"initiating hidden-service v2 replication fetch");
+      break;
+    case DIR_PURPOSE_UPLOAD_VOTE:
+      log_debug(LD_OR,"initiating server vote upload");
+      break;
+    case DIR_PURPOSE_UPLOAD_SIGNATURES:
+      log_debug(LD_OR,"initiating consensus signature upload");
+      break;
     case DIR_PURPOSE_FETCH_RUNNING_LIST:
       log_debug(LD_DIR,"initiating running-routers fetch");
       break;
@@ -567,9 +540,10 @@
       tor_assert(0);
   }
 
+log_warn(LD_REND, "directory_initiate_command 2");
   conn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR, AF_INET));
 
-  // TODO114 this is a (dirty) workaround!
+  /* This is a (dirty) workaround, but otherwise we don't get the conn... */
   if (dir_purpose == DIR_PURPOSE_FETCH_RENDDESC_V2) {
     tor_assert(payload);
     strlcpy(conn->rend_query, payload, sizeof(conn->rend_query));
@@ -743,6 +717,18 @@
       httpcommand = "POST";
       url = tor_strdup("/tor/");
       break;
+    case DIR_PURPOSE_UPLOAD_VOTE:
+      tor_assert(!resource);
+      tor_assert(payload);
+      httpcommand = "POST";
+      url = tor_strdup("/tor/post/vote");
+      break;
+    case DIR_PURPOSE_UPLOAD_SIGNATURES:
+      tor_assert(!resource);
+      tor_assert(payload);
+      httpcommand = "POST";
+      url = tor_strdup("/tor/post/vote");
+      break;
     case DIR_PURPOSE_FETCH_RENDDESC:
       tor_assert(resource);
       tor_assert(!payload);
@@ -769,6 +755,15 @@
       url = tor_malloc(len);
       tor_snprintf(url, len, "/tor/rendezvous2/%s", resource);
       break;
+    case DIR_PURPOSE_REPLICATE_RENDDESC_V2:
+      tor_assert(resource);
+      tor_assert(!payload);
+      tor_assert(strlen(resource) <= 2*REND_DESC_ID_V2_LEN+1);
+      httpcommand = "GET";
+      len = strlen(resource)+32;
+      url = tor_malloc(len);
+      tor_snprintf(url, len, "/tor/rendezvous2/%s", resource);
+      break;
     case DIR_PURPOSE_UPLOAD_RENDDESC:
       tor_assert(!resource);
       tor_assert(payload);
@@ -779,7 +774,7 @@
       tor_assert(!resource);
       tor_assert(payload);
       httpcommand = "POST";
-      url = tor_strdup("/tor/rendezvous/publish2");
+      url = tor_strdup("/tor/rendezvous2/publish");
       break;
     default:
       tor_assert(0);
@@ -1440,6 +1435,54 @@
      * dirservers down just because they don't like us. */
   }
 
+  if (conn->_base.purpose == DIR_PURPOSE_UPLOAD_VOTE) {
+    switch (status_code) {
+      case 200: {
+        log_notice(LD_DIR,"Uploaded a vote to dirserver %s:%d",
+                   conn->_base.address, conn->_base.port);
+        }
+        break;
+      case 400:
+        log_warn(LD_DIR,"http status 400 (%s) response after uploading "
+                 "vote to dirserver '%s:%d'. Please correct.",
+                 escaped(reason), conn->_base.address, conn->_base.port);
+        break;
+      default:
+        log_warn(LD_GENERAL,
+             "http status %d (%s) reason unexpected while uploading "
+             "vote to server '%s:%d').",
+             status_code, escaped(reason), conn->_base.address,
+             conn->_base.port);
+        break;
+    }
+    /* return 0 in all cases, since we don't want to mark any
+     * dirservers down just because they don't like us. */
+  }
+
+  if (conn->_base.purpose == DIR_PURPOSE_UPLOAD_SIGNATURES) {
+    switch (status_code) {
+      case 200: {
+        log_notice(LD_DIR,"Uploaded a signatures to dirserver %s:%d",
+                   conn->_base.address, conn->_base.port);
+        }
+        break;
+      case 400:
+        log_warn(LD_DIR,"http status 400 (%s) response after uploading "
+                 "signatures to dirserver '%s:%d'. Please correct.",
+                 escaped(reason), conn->_base.address, conn->_base.port);
+        break;
+      default:
+        log_warn(LD_GENERAL,
+             "http status %d (%s) reason unexpected while uploading "
+             "signatures to server '%s:%d').",
+             status_code, escaped(reason), conn->_base.address,
+             conn->_base.port);
+        break;
+    }
+    /* return 0 in all cases, since we don't want to mark any
+     * dirservers down just because they don't like us. */
+  }
+
   if (conn->_base.purpose == DIR_PURPOSE_FETCH_RENDDESC) {
     log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d "
              "(%s))",
@@ -1447,7 +1490,7 @@
     switch (status_code) {
       case 200:
         if (rend_cache_store(body, body_len, 0) < 0) {
-          log_warn(LD_REND,"Failed to store rendezvous descriptor.");
+          log_warn(LD_REND, "Failed to fetch rendezvous descriptor.");
           /* alice's ap_stream will notice when connection_mark_for_close
            * cleans it up */
         } else {
@@ -1475,14 +1518,18 @@
   }
 
   if (conn->_base.purpose == DIR_PURPOSE_FETCH_RENDDESC_V2) {
+    log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d "
+             "(%s))",
+             (int)body_len, status_code, escaped(reason));
     switch (status_code) {
       case 200:
         if (rend_cache_store_v2_client(body, conn->secret_cookie) < 0) {
-          log_warn(LD_REND,"Failed to store rendezvous descriptor.");
+          log_warn(LD_REND,"Fetching v2 descriptor failed.");
           /* alice's ap_stream will notice when connection_mark_for_close
            * cleans it up */
         } else {
           /* success. notify pending connections about this. */
+          log_info(LD_REND, "Successfully fetched descriptor.");
           conn->_base.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC;
           rend_client_desc_here(conn->rend_query);
         }
@@ -1492,20 +1539,52 @@
          * connection_mark_for_close cleans it up. */
         break;
       case 400:
-        log_warn(LD_REND,
+        log_warn(LD_REND, "Fetching v2 descriptor failed: "
                  "http status 400 (%s). Dirserver didn't like our "
                  "v2 rendezvous query?", escaped(reason));
         break;
       default:
-        log_warn(LD_REND,"http status %d (%s) response unexpected while "
+        log_warn(LD_REND, "Fetching v2 descriptor failed: "
+                 "http status %d (%s) response unexpected while "
                  "fetching v2 hidden service descriptor (server '%s:%d').",
                  status_code, escaped(reason), conn->_base.address,
                  conn->_base.port);
         break;
     }
   }
+  
+  if (conn->_base.purpose == DIR_PURPOSE_REPLICATE_RENDDESC_V2) {
+    log_info(LD_REND, "Received replicas of rendezvous descriptors (size %d, "
+                      "status %d (%s))",
+             (int)body_len, status_code, escaped(reason));
+    switch (status_code) {
+      case 200:
+        if (rend_cache_store_v2_dir(body) < 0) {
+          log_warn(LD_REND, "Fetching v2 descriptor replicas failed.");
+        } else {
+          log_info(LD_REND, "Successfully fetched replicas.");
+        }
+        break;
+      case 400:
+        log_warn(LD_REND, "Fetching v2 descriptor replicas failed: "
+                 "http status 400 (%s). Dirserver didn't like our "
+                 "v2 rendezvous query?", escaped(reason));
+        break;
+      default:
+        log_warn(LD_REND, "Fetching v2 descriptor replicas failed: "
+                 "http status %d (%s) response unexpected while "
+                 "fetching v2 replicas (server '%s:%d').",
+                 status_code, escaped(reason), conn->_base.address,
+                 conn->_base.port);
+        break;
+    }
+  }
 
-  if (conn->_base.purpose == DIR_PURPOSE_UPLOAD_RENDDESC) {
+  if (conn->_base.purpose == DIR_PURPOSE_UPLOAD_RENDDESC ||
+      conn->_base.purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) {
+    log_info(LD_REND,"Uploaded rendezvous descriptor (status %d "
+             "(%s))",
+             status_code, escaped(reason));
     switch (status_code) {
       case 200:
         log_info(LD_REND,
@@ -1809,7 +1888,8 @@
       /* try to get a new one now */
       if (!already_fetching_directory(DIR_PURPOSE_FETCH_DIR) &&
           !should_delay_dir_fetches(options))
-        directory_get_from_dirserver(DIR_PURPOSE_FETCH_DIR, NULL, 1);
+        directory_get_from_dirserver(DIR_PURPOSE_FETCH_DIR,
+                                     ROUTER_PURPOSE_GENERAL, NULL, 1);
       tor_free(url);
       return 0;
     }
@@ -1860,7 +1940,8 @@
       /* try to get a new one now */
       if (!already_fetching_directory(DIR_PURPOSE_FETCH_RUNNING_LIST) &&
           !should_delay_dir_fetches(options))
-        directory_get_from_dirserver(DIR_PURPOSE_FETCH_RUNNING_LIST, NULL, 1);
+        directory_get_from_dirserver(DIR_PURPOSE_FETCH_RUNNING_LIST,
+                                     ROUTER_PURPOSE_GENERAL, NULL, 1);
       tor_free(url);
       return 0;
     }
@@ -1889,25 +1970,35 @@
     return 0;
   }
 
-  if (!strcmpstart(url,"/tor/status/")) {
-    /* v2 network status fetch. */
+  if (!strcmpstart(url,"/tor/status/")
+      || !strcmp(url, "/tor/status-vote/current/consensus")
+      || !strcmp(url, "/tor/status-vote/current/consensus.z")) {
+    /* v2 or v3 network status fetch. */
     size_t url_len = strlen(url);
     int deflated = !strcmp(url+url_len-2, ".z");
     smartlist_t *dir_fps = smartlist_create();
+    int is_v3 = !strcmpstart(url, "/tor/status-vote");
     const char *request_type = NULL;
     const char *key = url + strlen("/tor/status/");
     if (deflated)
       url[url_len-2] = '\0';
-    dirserv_get_networkstatus_v2_fingerprints(dir_fps, key);
-    if (!strcmpstart(key, "fp/"))
-      request_type = deflated?"/tor/status/fp.z":"/tor/status/fp";
-    else if (!strcmpstart(key, "authority"))
-      request_type = deflated?"/tor/status/authority.z":
-        "/tor/status/authority";
-    else if (!strcmpstart(key, "all"))
-      request_type = deflated?"/tor/status/all.z":"/tor/status/all";
-    else
-      request_type = "/tor/status/?";
+    if (!is_v3) {
+      dirserv_get_networkstatus_v2_fingerprints(dir_fps, key);
+      if (!strcmpstart(key, "fp/"))
+        request_type = deflated?"/tor/status/fp.z":"/tor/status/fp";
+      else if (!strcmpstart(key, "authority"))
+        request_type = deflated?"/tor/status/authority.z":
+          "/tor/status/authority";
+      else if (!strcmpstart(key, "all"))
+        request_type = deflated?"/tor/status/all.z":"/tor/status/all";
+      else
+        request_type = "/tor/status/?";
+    } else {
+      smartlist_add(dir_fps, tor_memdup("\0\0\0\0\0\0\0\0\0\0"
+                                        "\0\0\0\0\0\0\0\0\0\0", 20));
+      request_type = deflated?"v3.z":"v3";
+    }
+
     tor_free(url);
     if (!smartlist_len(dir_fps)) { /* we failed to create/cache cp */
       write_http_status_line(conn, 503, "Network status object unavailable");
@@ -2018,42 +2109,52 @@
 
   if (options->HSDir &&
        !strcmpstart(url,"/tor/rendezvous2/")) {
-    /* v2 rendezvous descriptor fetch */
-    const char *descp;
-    size_t desc_len;
-    const char *query = url+strlen("/tor/rendezvous2/");
-
-    switch (rend_cache_lookup_v2_dir(query, &descp, &desc_len)) {
-      case 1: /* valid */
-        write_http_response_header(conn, desc_len, "text/plain",
+    /* Handle v2 rendezvous service fetch request. */
+    char *descp;
+    const char *query = url + strlen("/tor/rendezvous2/");
+    if (strlen(query) == REND_DESC_ID_V2_LEN) {
+      switch (rend_cache_lookup_v2_dir(query, &descp)) {
+        case 1: /* valid */
+          write_http_response_header(conn, strlen(descp), "text/plain",
+                                     NULL, 0);
+          connection_write_to_buf(descp, strlen(descp), TO_CONN(conn));
+          tor_free(descp);
+          break;
+        case 0: /* well-formed but not present */
+          write_http_status_line(conn, 404, "Not found");
+          break;
+        case -1: /* not well-formed */
+          write_http_status_line(conn, 400, "Bad request");
+          break;
+      }
+    /* Handle v2 rendezvous service replica request. */
+    } else if (strlen(query) == 2 * REND_DESC_ID_V2_LEN + 1) {
+      if (rend_cache_lookup_v2_replicas(query, &descp) < 0) {
+      	/* not well-formed */
+      	write_http_status_line(conn, 400, "Bad request");
+      } else {
+        /* valid */
+        write_http_response_header(conn, strlen(descp), "text/plain",
                                    NULL, 0);
-        /* need to send descp separately, because it may include nuls */
-        connection_write_to_buf(descp, desc_len, TO_CONN(conn));
-        break;
-      case 0: /* well-formed but not present */
-        write_http_status_line(conn, 404, "Not found");
-        break;
-      case -1: /* not well-formed */
-        write_http_status_line(conn, 400, "Bad request");
-        break;
+        connection_write_to_buf(descp, strlen(descp), TO_CONN(conn));
+        tor_free(descp);
+      }
+    } else { /* not well-formed */
+      write_http_status_line(conn, 400, "Bad request");
     }
     tor_free(url);
     return 0;
   }
 
   if (options->HSAuthoritativeDir &&
-      (!strcmpstart(url,"/tor/rendezvous/") ||
-       !strcmpstart(url,"/tor/rendezvous1/"))) {
+       !strcmpstart(url,"/tor/rendezvous/")) {
     /* rendezvous descriptor fetch */
     const char *descp;
     size_t desc_len;
-    int versioned = !strcmpstart(url,"/tor/rendezvous1/");
-    const char *query = url+strlen("/tor/rendezvous/")+(versioned?1:0);
+    const char *query = url+strlen("/tor/rendezvous/");
 
-    // TODO114 it is not correct to allow all versions, but only
-    // 0 or 1; what if we were by chance a v2 client for that service? then we
-    // should better not return the v2 descriptor as result to a 0/1 request!
-    switch (rend_cache_lookup_desc(query, versioned?-1:0, &descp, &desc_len)) {
+    log_info(LD_REND, "Handling rendezvous descriptor get");
+    switch (rend_cache_lookup_desc(query, 0, &descp, &desc_len)) {
       case 1: /* valid */
         write_http_response_header(conn, desc_len, "application/octet-stream",
                                    NULL, 0);
@@ -2155,12 +2256,12 @@
   }
   log_debug(LD_DIRSERV,"rewritten url as '%s'.", url);
 
+  /* Handle v2 rendezvous service publish request. */
   if (options->HSDir &&
-     !strcmpstart(url,"/tor/rendezvous/publish2")) {
-    /* rendezvous descriptor post */
-    if (rend_cache_store_v2_dir(body) < 0) {
-      log_fn(LOG_PROTOCOL_WARN, LD_DIRSERV,
-             "Rejected rend descriptor (length %d) from %s.",
+      !strcmpstart(url,"/tor/rendezvous2/publish")) {
+    log_info(LD_REND, "Handling v2 rendezvous descriptor post");
+    if (rend_cache_store_v2_dir(body) <= 0) {
+      log_warn(LD_REND, "Rejected rend descriptor (length %d) from %s.",
              (int)body_len, conn->_base.address);
       write_http_status_line(conn, 400, "Invalid service descriptor rejected");
     } else {
@@ -2206,9 +2307,11 @@
 
   if (options->HSAuthoritativeDir &&
       !strcmpstart(url,"/tor/rendezvous/publish")) {
+
+    log_info(LD_REND, "Handling rendezvous descriptor post.");
+
     /* rendezvous descriptor post */
     if (rend_cache_store(body, body_len, 1) < 0) {
-//      char tmp[1024*2+1];
       log_fn(LOG_PROTOCOL_WARN, LD_DIRSERV,
              "Rejected rend descriptor (length %d) from %s.",
              (int)body_len, conn->_base.address);
@@ -2219,6 +2322,28 @@
     goto done;
   }
 
+  if (authdir_mode_v3(options) &&
+      !strcmp(url,"/tor/post/vote")) { /* server descriptor post */
+    const char *msg = "OK";
+    if (dirvote_add_vote(body, &msg)) {
+      write_http_status_line(conn, 200, "Vote stored");
+    } else {
+      tor_assert(msg);
+      write_http_status_line(conn, 400, msg);
+    }
+    goto done;
+  }
+
+  if (authdir_mode_v3(options) &&
+      !strcmp(url,"/tor/post/consensus-signature")) { /* sigs on consensus. */
+    if (dirvote_add_signatures(body)>=0) {
+      write_http_status_line(conn, 200, "Signatures stored");
+    } else {
+      write_http_status_line(conn, 400, "Unable to store signatures");
+    }
+    goto done;
+  }
+
   /* we didn't recognize the url */
   write_http_status_line(conn, 404, "Not found");
 
@@ -2484,3 +2609,190 @@
   return 0;
 }
 
+/** Determine the responsible hidden service directories for <b>desc_ids</b>
+ * and upload the appropriate descriptor from <b>descs</b> to them.
+ *
+ * TODO114 enable tunneling when available!!
+ */
+void
+directory_post_to_hs_dir(const char *service_id, char desc_ids[][20],
+                         smartlist_t *descs, int seconds_valid)
+{
+  int i, j;
+  routerstatus_t *hs_dirs[NUMBER_OF_CONSECUTIVE_REPLICAS];
+  routerstatus_t *hs_dir;
+log_warn(LD_REND, "directory_post_to_hs_dir 1");
+  for (i = 0; i < NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) {
+log_warn(LD_REND, "directory_post_to_hs_dir 2");
+  	/* Determine responsible dirs. */
+    if (get_responsible_hs_dirs(desc_ids[i], hs_dirs) < 0) {
+      log_warn(LD_REND, "Could not determine the responsible hidden service "
+                        "directories to post descriptors to.");
+      return;
+    }
+log_warn(LD_REND, "directory_post_to_hs_dir 3");
+    for (j = 0; j < NUMBER_OF_CONSECUTIVE_REPLICAS; j++) {
+      hs_dir = hs_dirs[j];
+log_warn(LD_REND, "directory_post_to_hs_dir 4");
+      /* Send publish request. */
+      directory_initiate_command_routerstatus(hs_dir,
+                                              DIR_PURPOSE_UPLOAD_RENDDESC_V2,
+                                              ROUTER_PURPOSE_GENERAL, 0,
+                                              NULL, descs->list[i],
+                                              strlen(descs->list[i]));
+      log_info(LD_REND, "Sending publish request for v2 descriptor for "
+                        "service '%s' with descriptor ID '%s' with validity "
+                        "of %d seconds to hidden service directory '%s' on "
+                        "port %d.",
+               service_id,
+               desc_ids[i],
+               seconds_valid,
+               hs_dir->nickname,
+               hs_dir->dir_port);
+    }
+log_warn(LD_REND, "directory_post_to_hs_dir 5");
+  }
+log_warn(LD_REND, "directory_post_to_hs_dir 6");
+}
+
+/** Determine the responsible hidden service directories for <b>desc_id</b>
+ * and that descriptor from one of them; <b>query</b> and <b>secret_cookie</b>
+ * are only passed for pretty log statements.
+ * 
+ * TODO114 enable tunneling when available!!
+ */
+void
+directory_get_from_hs_dir(const char *desc_id, const char *query,
+                          const char *secret_cookie)
+{
+  routerstatus_t *hs_dirs[NUMBER_OF_CONSECUTIVE_REPLICAS];
+  routerstatus_t *hs_dir;
+  char orig_request[16 + 1 + 24 + 1];
+  char desc_id_binary[REND_DESC_ID_V2_LEN + 1];
+  int replica;
+  tor_assert(desc_id);
+  tor_assert(query);
+  tor_assert(strlen(query) == 16);
+  tor_assert(secret_cookie);
+  tor_assert(strlen(secret_cookie) == 24);
+  /* Determine responsible dirs. */
+  if (get_responsible_hs_dirs(desc_id, hs_dirs) < 0) {
+    log_warn(LD_REND, "Could not determine the responsible hidden service "
+                      "directories to fetch descriptors.");
+    return;
+  }
+  replica = crypto_rand_int(NUMBER_OF_CONSECUTIVE_REPLICAS);
+  hs_dir = hs_dirs[replica];
+  /* TODO114 if hsdir fails, use another one... */
+  tor_snprintf(orig_request, sizeof(orig_request), "%s.%s", query,
+               secret_cookie);
+  base32_encode(desc_id_binary, REND_DESC_ID_V2_LEN + 1, desc_id, DIGEST_LEN);
+  /* Send fetch request. */
+  directory_initiate_command_routerstatus(hs_dir,
+                                          DIR_PURPOSE_FETCH_RENDDESC_V2,
+                                          ROUTER_PURPOSE_GENERAL, 0,
+                                          desc_id_binary, orig_request, 0);
+  log_info(LD_REND, "Sending fetch request for v2 descriptor for "
+                    "service '%s' with descriptor ID '%s' from hidden "
+                    "service directory '%s' on port %d.",
+           query, desc_id_binary, hs_dir->nickname, hs_dir->dir_port);
+}
+
+/** Send a fetch request for replicas in interval from <b>from_id</b> to
+ * <b>to_id</b> to hidden service directory <b>hs_dir</b>. */ 
+void
+hs_dir_fetch_replicas(routerstatus_t *hs_dir, const char *from_id,
+                                   const char *to_id)
+{
+  char request[REND_DESC_ID_V2_LEN + 1 + REND_DESC_ID_V2_LEN + 1];
+  tor_assert(hs_dir);
+  tor_assert(from_id);
+  tor_assert(to_id);
+  tor_assert(strlen(from_id) == 32);
+  tor_assert(strlen(to_id) == 32);
+  tor_snprintf(request, sizeof(request), "%s-%s", from_id, to_id);
+  directory_initiate_command_routerstatus(hs_dir,
+                                          DIR_PURPOSE_REPLICATE_RENDDESC_V2,
+                                          ROUTER_PURPOSE_GENERAL, 0,
+                                          request, NULL, 0);
+  log_info(LD_REND, "Sending replication request for v2 descriptors in "
+                    "interval '%s' to '%s' from hidden service "
+                    "directory '%s' on port %d.",
+           from_id,
+           to_id,
+           hs_dir->nickname,
+           hs_dir->dir_port);
+}
+
+/** If currently acting as hidden service directory, request replicas from
+ * predecessors, request descriptors for which this node is primarily
+ * responsible from successors, and clean up all descriptors for which this
+ * node is not (any more) responsible. */
+void
+hs_dir_perform_replication(void)
+{
+  const char *me;
+  const char *predecessor0;
+  const char *predecessor1;
+  char from_id_base32[REND_DESC_ID_V2_LEN+1];
+  char to_id_base32[REND_DESC_ID_V2_LEN+1];
+  int i;
+  const char *direct_predecessor;
+  const char *successor;
+  local_routerstatus_t *pred_router;
+  routerstatus_t *pred_status;
+  local_routerstatus_t *succ_router;
+  routerstatus_t *succ_status;
+  /* Check if I am acting as hidden service directory and there are enough
+   * other hidden service directories available; otherwise there is no
+   * replication necessary/possible. */
+log_info(LD_REND, "KL8 are we acting as hs dir? %d", acting_as_hs_dir());
+  if (!acting_as_hs_dir())
+    return;
+  /* Get descriptors of which I should hold replicas from
+   * NUMBER_OF_CONSECUTIVE_REPLICAS - 1 predecessors. */
+  me = router_get_my_routerinfo()->cache_info.identity_digest;
+  predecessor0 = me;
+  predecessor1 = previous_hs_dir(me);
+  for (i = 0; i < NUMBER_OF_CONSECUTIVE_REPLICAS - 1; i++) {
+    predecessor0 = predecessor1;
+    predecessor1 = previous_hs_dir(predecessor0);
+    pred_router = router_get_combined_status_by_digest(predecessor0);
+    pred_status = &(pred_router->status);
+    base32_encode(from_id_base32, REND_DESC_ID_V2_LEN + 1, predecessor1,
+                  DIGEST_LEN);
+    base32_encode(to_id_base32, REND_DESC_ID_V2_LEN + 1, predecessor0,
+                  DIGEST_LEN);
+    log_debug(LD_REND, "Requesting descriptors in interval %s to %s as "
+                       "replicas from predecessor.",
+              from_id_base32,
+              to_id_base32);
+log_info(LD_REND, "hs_dir_perform_replication 1");
+    hs_dir_fetch_replicas(pred_status, from_id_base32, to_id_base32);
+  }
+  /* Get descriptors for which I am primarily responsible from
+   * NUMBER_OF_CONSECUTIVE_REPLICAS - 1 successors. */
+  direct_predecessor = previous_hs_dir(me);
+  successor = next_hs_dir(me);
+  for (i = 0; i < NUMBER_OF_CONSECUTIVE_REPLICAS - 1; i++) {
+    succ_router = router_get_combined_status_by_digest(successor);
+    succ_status = &(succ_router->status);
+    base32_encode(from_id_base32, REND_DESC_ID_V2_LEN + 1, direct_predecessor,
+                  DIGEST_LEN);
+    base32_encode(to_id_base32, REND_DESC_ID_V2_LEN + 1, me,
+                  DIGEST_LEN);
+    log_debug(LD_REND, "Requesting descriptors in interval %s to %s for "
+                       "which I am primarily responsible from successor.",
+             from_id_base32,
+             to_id_base32);
+log_info(LD_REND, "hs_dir_perform_replication 2");
+    hs_dir_fetch_replicas(succ_status, from_id_base32, to_id_base32);
+    successor = next_hs_dir(successor);
+  }
+  /* Clean up descriptors for which I am not reponsible (neither primarily
+   * nor for replication). */
+  log_debug(LD_REND, "Cleaning up all descriptors that are not (any more) in "
+                     "the interval for which i am responsible.");
+  rend_cache_clean_up();
+}
+

Modified: tor/branches/114-dist-storage/src/or/dirserv.c
===================================================================
--- tor/branches/114-dist-storage/src/or/dirserv.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/dirserv.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -12,7 +12,7 @@
  * \file dirserv.c
  * \brief Directory server core implementation. Manages directory
  * contents and generates directories.
- **/
+ */
 
 /** How far in the future do we allow a router to get? (seconds) */
 #define ROUTER_ALLOW_SKEW (60*60*12)
@@ -32,6 +32,13 @@
 static int runningrouters_is_dirty = 1;
 static int the_v2_networkstatus_is_dirty = 1;
 
+/** Most recently generated encoded signed v1 directory. (v1 auth dirservers
+ * only.) */
+static cached_dir_t *the_directory = NULL;
+
+/** For authoritative directories: the current (v1) network status. */
+static cached_dir_t the_runningrouters = { NULL, NULL, 0, 0, 0, -1 };
+
 static void directory_remove_invalid(void);
 static cached_dir_t *dirserv_regenerate_directory(void);
 static char *format_versions_list(config_line_t *ln);
@@ -693,7 +700,8 @@
     if (r & FP_REJECT) {
       log_info(LD_DIRSERV, "Router '%s' is now rejected: %s",
                ent->nickname, msg?msg:"");
-      routerlist_remove(rl, ent, i--, 0);
+      routerlist_remove(rl, ent, 0);
+      i--;
       changed = 1;
       continue;
     }
@@ -771,11 +779,26 @@
 directory_set_dirty(void)
 {
   time_t now = time(NULL);
+  int set_v1_dirty=0;
 
-  if (!the_directory_is_dirty)
-    the_directory_is_dirty = now;
-  if (!runningrouters_is_dirty)
-    runningrouters_is_dirty = now;
+#ifdef FULL_V1_DIRECTORIES
+  set_v1_dirty = 1;
+#else
+  /* Regenerate stubs only every 8 hours. XXXX020 */
+#define STUB_REGENERATE_INTERVAL (8*60*60)
+  if (!the_directory || !the_runningrouters.dir)
+    set_v1_dirty = 1;
+  else if (the_directory->published < now - STUB_REGENERATE_INTERVAL ||
+           the_runningrouters.published < now - STUB_REGENERATE_INTERVAL)
+    set_v1_dirty = 1;
+#endif
+
+  if (set_v1_dirty) {
+    if (!the_directory_is_dirty)
+      the_directory_is_dirty = now;
+    if (!runningrouters_is_dirty)
+      runningrouters_is_dirty = now;
+  }
   if (!the_v2_networkstatus_is_dirty)
     the_v2_networkstatus_is_dirty = now;
 }
@@ -946,14 +969,22 @@
   char *buf = NULL;
   size_t buf_len;
   size_t identity_pkey_len;
+  time_t now = time(NULL);
+#ifdef FULL_V1_DIRECTORIES
   routerlist_t *rl = router_get_routerlist();
-  time_t now = time(NULL);
+#else
+  (void)complete;
+#endif
 
   tor_assert(dir_out);
   *dir_out = NULL;
 
+#ifdef FULL_V1_DIRECTORIES
   if (list_server_status(rl->routers, &router_status, 0))
     return -1;
+#else
+  router_status = tor_strdup("");
+#endif
 
   if (crypto_pk_write_public_key_to_string(private_key,&identity_pkey,
                                            &identity_pkey_len)<0) {
@@ -968,9 +999,11 @@
 
   buf_len = 2048+strlen(recommended_versions)+
     strlen(router_status);
+#ifdef FULL_V1_DIRECTORIES
   SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri,
                     if (complete || router_is_active(ri, now))
                       buf_len += ri->cache_info.signed_descriptor_len+1);
+#endif
   buf = tor_malloc(buf_len);
   /* We'll be comparing against buf_len throughout the rest of the
      function, though strictly speaking we shouldn't be able to exceed
@@ -991,6 +1024,7 @@
   tor_free(identity_pkey);
 
   cp = buf + strlen(buf);
+#ifdef FULL_V1_DIRECTORIES
   SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri,
     {
       size_t len = ri->cache_info.signed_descriptor_len;
@@ -1005,6 +1039,7 @@
       *cp++ = '\n'; /* add an extra newline in case somebody was depending on
                      * it. */
     });
+#endif
   *cp = '\0';
 
   /* These multiple strlcat calls are inefficient, but dwarfed by the RSA
@@ -1035,10 +1070,6 @@
   return -1;
 }
 
-/** Most recently generated encoded signed v1 directory. (v1 auth dirservers
- * only.) */
-static cached_dir_t *the_directory = NULL;
-
 /* Used only by non-v1-auth dirservers: The v1 directory and
  * runningrouters we'll serve when requested. */
 static cached_dir_t *cached_directory = NULL;
@@ -1048,6 +1079,9 @@
  * cached_dir_t. */
 static digestmap_t *cached_v2_networkstatus = NULL;
 
+/** DOCDOC */
+static cached_dir_t *cached_v3_networkstatus = NULL;
+
 /** Possibly replace the contents of <b>d</b> with the value of
  * <b>directory</b> published on <b>when</b>, unless <b>when</b> is older than
  * the last value, or too far in the future.
@@ -1092,7 +1126,7 @@
 
 /** Allocate and return a new cached_dir_t containing the string <b>s</b>,
  * published at <b>published</b>. */
-static cached_dir_t *
+cached_dir_t *
 new_cached_dir(char *s, time_t published)
 {
   cached_dir_t *d = tor_malloc_zero(sizeof(cached_dir_t));
@@ -1214,6 +1248,17 @@
   }
 }
 
+/* DOCDOC */
+void
+dirserv_set_cached_networkstatus_v3(const char *networkstatus,
+                                    time_t published)
+{
+  if (cached_v3_networkstatus)
+    cached_dir_decref(cached_v3_networkstatus);
+  cached_v3_networkstatus = new_cached_dir(
+                                  tor_strdup(networkstatus), published);
+}
+
 /** Remove any v2 networkstatus from the directory cache that was published
  * before <b>cutoff</b>. */
 void
@@ -1341,9 +1386,6 @@
   return the_directory;
 }
 
-/** For authoritative directories: the current (v1) network status. */
-static cached_dir_t the_runningrouters = { NULL, NULL, 0, 0, 0, -1 };
-
 /** Only called by v1 auth dirservers.
  * Replace the current running-routers list with a newly generated one. */
 static cached_dir_t *
@@ -1357,11 +1399,17 @@
   crypto_pk_env_t *private_key = get_identity_key();
   char *identity_pkey; /* Identity key, DER64-encoded. */
   size_t identity_pkey_len;
+#ifdef FULL_V1_DIRECTORIES
   routerlist_t *rl = router_get_routerlist();
+#endif
 
+#ifdef FULL_V1_DIRECTORIES
   if (list_server_status(rl->routers, &router_status, 0)) {
     goto err;
   }
+#else
+  router_status = tor_strdup("");
+#endif
   if (crypto_pk_write_public_key_to_string(private_key,&identity_pkey,
                                            &identity_pkey_len)<0) {
     log_warn(LD_BUG,"write identity_pkey to string failed!");
@@ -1412,6 +1460,12 @@
                          "v1 network status list", V1_AUTHORITY);
 }
 
+cached_dir_t *
+dirserv_get_consensus(void)
+{
+  return cached_v3_networkstatus;
+}
+
 /** For authoritative directories: the current (v2) network status. */
 static cached_dir_t *the_v2_networkstatus = NULL;
 
@@ -1436,6 +1490,12 @@
  * network using allegedly high-uptime nodes, displacing all the
  * current guards. */
 #define UPTIME_TO_GUARANTEE_STABLE (3600*24*30)
+/** Similarly, we protect sufficiently fast nodes from being pushed
+ * out of the set of Fast nodes. */
+#define BANDWIDTH_TO_GUARANTEE_FAST (100*1024)
+/** Similarly, every node with sufficient bandwidth can be considered
+ * for Guard status. */
+#define BANDWIDTH_TO_GUARANTEE_GUARD (250*1024)
 
 /* Thresholds for server performance: set by
  * dirserv_compute_performance_thresholds, and used by
@@ -1474,12 +1534,31 @@
         (unsigned)uptime < UPTIME_TO_GUARANTEE_STABLE)
       return 1;
   }
-  if (need_capacity &&
-      router_get_advertised_bandwidth(router) < fast_bandwidth)
-    return 1;
+  if (need_capacity) {
+    uint32_t bw = router_get_advertised_bandwidth(router);
+    if (bw < fast_bandwidth && bw < BANDWIDTH_TO_GUARANTEE_FAST)
+      return 1;
+  }
   return 0;
 }
 
+/** Return 1 if <b>router</b> has an uptime of at least 24 hours and is
+ * reachable, else 0.
+ */
+static int
+dirserv_thinks_router_is_hs_dir(routerinfo_t *router, time_t now)
+{
+  int uptime = real_uptime(router, now);
+
+
+
+  return (router->wants_to_be_hs_dir &&
+          (unsigned)uptime > MIN_UPTIME_HS_DIR &&
+          ((router_is_me(router) && !we_are_hibernating()) ||
+           (now < router->last_reachable + HS_DIR_REACHABLE_TIMEOUT)));
+}
+
+
 /** Helper: returns a tristate based on comparing **(uint32_t**)<b>a</b>
  * to **(uint32_t**)<b>b</b>. */
 static int
@@ -1678,6 +1757,86 @@
                 DIGEST_LEN);
 }
 
+/** DOCDOC
+ *
+ * sort first by addr, and then by descending order of usefulness.
+ **/
+static int
+_compare_routerinfo_by_ip_and_bw(const void **a, const void **b)
+{
+  routerinfo_t *first = *(routerinfo_t **)a, *second = *(routerinfo_t **)b;
+  int first_is_auth, second_is_auth;
+  uint32_t bw_first, bw_second;
+
+  /* we return -1 if first should appear before second... that is,
+   * if first is a better router. */
+  if (first->addr < second->addr)
+    return -1;
+  else if (first->addr > second->addr)
+    return 1;
+
+  /* XXX020 k n lg n memcmps could show up bigtime in profiling. If
+   * they do, I suggest we just give authorities a free pass. -RD */
+  /* I think we're fine.  Remember, in nearly all cases, the addresses
+   * will be different and we'll never actually reach this point. -NM */
+
+  first_is_auth =
+    router_digest_is_trusted_dir(first->cache_info.identity_digest);
+  second_is_auth =
+    router_digest_is_trusted_dir(second->cache_info.identity_digest);
+
+  if (first_is_auth && !second_is_auth)
+    return -1;
+  else if (!first_is_auth && second_is_auth)
+    return 1;
+
+  else if (first->is_running && !second->is_running)
+    return -1;
+  else if (!first->is_running && second->is_running)
+    return 1;
+
+  bw_first = router_get_advertised_bandwidth(first);
+  bw_second = router_get_advertised_bandwidth(second);
+
+  if (bw_first > bw_second)
+     return -1;
+  else if (bw_first < bw_second)
+    return 1;
+
+  /* They're equal! Compare by identity digest, so there's a
+   * deterministic order and we avoid flapping. */
+  return _compare_routerinfo_by_id_digest(a, b);
+}
+
+/** DOCDOC takes list of routerinfo */
+static digestmap_t *
+get_possible_sybil_list(const smartlist_t *routers)
+{
+  digestmap_t *omit_as_sybil;
+  smartlist_t *routers_by_ip = smartlist_create();
+  uint32_t last_addr;
+  int addr_count;
+  smartlist_add_all(routers_by_ip, routers);
+  smartlist_sort(routers_by_ip, _compare_routerinfo_by_ip_and_bw);
+  omit_as_sybil = digestmap_new();
+
+#define MAX_WITH_SAME_ADDR 3
+  last_addr = 0;
+  addr_count = 0;
+  SMARTLIST_FOREACH(routers_by_ip, routerinfo_t *, ri,
+    {
+      if (last_addr != ri->addr) {
+        last_addr = ri->addr;
+        addr_count = 1;
+      } else if (++addr_count > MAX_WITH_SAME_ADDR) {
+        digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri);
+      }
+    });
+
+  smartlist_free(routers_by_ip);
+  return omit_as_sybil;
+}
+
 /** DOCDOC */
 static void
 set_routerstatus_from_routerinfo(routerstatus_t *rs,
@@ -1710,11 +1869,13 @@
   rs->is_valid = ri->is_valid;
   rs->is_possible_guard = rs->is_fast && rs->is_stable &&
     (!rs->is_exit || exits_can_be_guards) &&
-    router_get_advertised_bandwidth(ri) >=
-    (exits_can_be_guards ? guard_bandwidth_including_exits :
-     guard_bandwidth_excluding_exits);
+    (router_get_advertised_bandwidth(ri) >= BANDWIDTH_TO_GUARANTEE_GUARD ||
+     router_get_advertised_bandwidth(ri) >=
+     (exits_can_be_guards ? guard_bandwidth_including_exits :
+      guard_bandwidth_excluding_exits));
   rs->is_bad_exit = listbadexits && ri->is_bad_exit;
-  rs->is_hs_dir = ri->is_hs_dir; /* TODO only the very first approach... */
+  ri->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, now);
+  rs->is_hs_dir = ri->is_hs_dir;
   /* 0.1.1.9-alpha is the first version to support fetch by descriptor
    * hash. */
   rs->is_v2_dir = ri->dir_port &&
@@ -1753,6 +1914,8 @@
   time_t now = time(NULL);
   time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
   networkstatus_voter_info_t *voter = NULL;
+  vote_timing_t timing;
+  digestmap_t *omit_as_sybil = NULL;
 
   /* check that everything is deallocated XXXX020 */
 
@@ -1796,6 +1959,7 @@
   routers = smartlist_create();
   smartlist_add_all(routers, rl->routers);
   smartlist_sort(routers, _compare_routerinfo_by_id_digest);
+  omit_as_sybil = get_possible_sybil_list(routers);
 
   routerstatuses = smartlist_create();
 
@@ -1810,21 +1974,31 @@
                                        naming, exits_can_be_guards,
                                        listbadexits);
 
+      if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest)) {
+        rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
+          rs->is_running = rs->is_named = rs->is_valid = rs->is_v2_dir =
+          rs->is_possible_guard = 0;
+      }
+
       vrs->version = version_from_platform(ri->platform);
       smartlist_add(routerstatuses, vrs);
     }
   });
   smartlist_free(routers);
+  digestmap_free(omit_as_sybil, NULL);
 
   tor_assert(v3_out);
   memset(v3_out, 0, sizeof(networkstatus_vote_t));
   v3_out->is_vote = 1;
-  v3_out->published = time(NULL);
-  v3_out->valid_after = time(NULL); /* XXXX020 not right. */
-  v3_out->fresh_until = time(NULL); /* XXXX020 not right. */
-  v3_out->valid_until = time(NULL); /* XXXX020 not right. */
-  v3_out->vote_seconds = 600; /* XXXX020 not right. */
-  v3_out->dist_seconds = 600; /* XXXX020 not right. */
+  dirvote_get_preferred_voting_intervals(&timing);
+  v3_out->published = now;
+  v3_out->valid_after =
+    dirvote_get_start_of_next_interval(now, timing.vote_interval);
+  v3_out->fresh_until = v3_out->valid_after + timing.vote_interval;
+  v3_out->valid_until = v3_out->valid_after +
+    (timing.vote_interval * timing.n_intervals_valid);
+  v3_out->vote_seconds = timing.vote_delay;
+  v3_out->dist_seconds = timing.dist_delay;
 
   v3_out->client_versions = client_versions;
   v3_out->server_versions = server_versions;
@@ -1950,6 +2124,7 @@
     tor_snprintf(status, len,
                  "network-status-version 3\n"
                  "vote-status vote\n"
+                 "consensus-methods 1\n"
                  "published %s\n"
                  "valid-after %s\n"
                  "fresh-until %s\n"
@@ -2035,7 +2210,9 @@
   return status;
 }
 
-static cached_dir_t *
+/** DOCDOC */
+/* XXXX020 possibly rename and relocate to dirvote.c? */
+cached_dir_t *
 generate_v3_networkstatus(void)
 {
   crypto_pk_env_t *key = get_my_v3_authority_signing_key();
@@ -2113,6 +2290,7 @@
   const char *contact;
   char *version_lines = NULL;
   smartlist_t *routers = NULL;
+  digestmap_t *omit_as_sybil = NULL;
 
   if (!v2)
     return generate_v3_networkstatus();
@@ -2198,6 +2376,8 @@
   smartlist_add_all(routers, rl->routers);
   smartlist_sort(routers, _compare_routerinfo_by_id_digest);
 
+  omit_as_sybil = get_possible_sybil_list(routers);
+
   SMARTLIST_FOREACH(routers, routerinfo_t *, ri, {
     if (ri->cache_info.published_on >= cutoff) {
       routerstatus_t rs;
@@ -2207,6 +2387,12 @@
                                        naming, exits_can_be_guards,
                                        listbadexits);
 
+      if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest)) {
+        rs.is_authority = rs.is_exit = rs.is_stable = rs.is_fast =
+          rs.is_running = rs.is_named = rs.is_valid = rs.is_v2_dir =
+          rs.is_possible_guard = 0;
+      }
+
       if (routerstatus_format_entry(outp, endp-outp, &rs, version, 0)) {
         log_warn(LD_BUG, "Unable to print router status.");
         tor_free(version);
@@ -2263,6 +2449,8 @@
   tor_free(identity_pkey);
   if (routers)
     smartlist_free(routers);
+  if (omit_as_sybil)
+    digestmap_free(omit_as_sybil, NULL);
   return r;
 }
 
@@ -2761,7 +2949,9 @@
       /* Add another networkstatus; start serving it. */
       char *fp = smartlist_pop_last(conn->fingerprint_stack);
       cached_dir_t *d;
-      if (router_digest_is_me(fp))
+      if (tor_digest_is_zero(fp)) /* XXXX020 document this "feature". */
+        d = cached_v3_networkstatus;
+      else if (router_digest_is_me(fp))
         d = the_v2_networkstatus;
       else
         d = digestmap_get(cached_v2_networkstatus, fp);

Modified: tor/branches/114-dist-storage/src/or/main.c
===================================================================
--- tor/branches/114-dist-storage/src/or/main.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/main.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -61,6 +61,8 @@
 static time_t time_to_fetch_running_routers = 0;
 /** When do we next launch DNS wildcarding checks? */
 static time_t time_to_check_for_correct_dns = 0;
+/** When do we next clean the rep history and rend cache? */
+static time_t time_to_clean_rep_history_and_rend_cache = 0;
 
 /** How often will we honor SIGNEWNYM requests? */
 #define MAX_SIGNEWNYM_RATE 10
@@ -118,6 +120,7 @@
 /** If our router descriptor ever goes this long without being regenerated
  * because something changed, we force an immediate regenerate-and-upload. */
 #define FORCE_REGENERATE_DESCRIPTOR_INTERVAL (18*60*60)
+//#define FORCE_REGENERATE_DESCRIPTOR_INTERVAL (60)
 /** How often do we check whether part of our router info has changed in a way
  * that would require an upload? */
 #define CHECK_DESCRIPTOR_INTERVAL (60)
@@ -144,6 +147,11 @@
 #define TLS_HANDSHAKE_TIMEOUT           (60)
 /** How often do we write hidden service usage statistics to disk? */
 #define WRITE_HSUSAGE_INTERVAL (900)
+/** How often do we update our hidden service directory routing table? */
+#define UPDATE_HS_DIRS_INTERVAL (60)
+/** How often do we perform replication of hidden service descriptors, if we
+ * are a hidden service directory? */
+#define HS_DIR_REPLICATION_INTERVAL (60*60)
 
 /********* END VARIABLES ************/
 
@@ -165,7 +173,7 @@
   tor_assert(conn->s >= 0 ||
              conn->linked ||
              (conn->type == CONN_TYPE_AP &&
-              TO_EDGE_CONN(conn)->dns_server_request));
+              TO_EDGE_CONN(conn)->is_dns_request));
 
   tor_assert(conn->conn_array_index == -1); /* can only connection_add once */
   conn->conn_array_index = smartlist_len(connection_array);
@@ -564,18 +572,20 @@
 {
   connection_t *conn;
   int retval;
+  time_t now;
 
   conn = smartlist_get(connection_array, i);
   if (!conn->marked_for_close)
     return 0; /* nothing to see here, move along */
-  assert_connection_ok(conn, time(NULL));
+  now = time(NULL);
+  assert_connection_ok(conn, now);
   assert_all_pending_dns_resolves_ok();
 
   log_debug(LD_NET,"Cleaning up connection (fd %d).",conn->s);
   if ((conn->s >= 0 || conn->linked_conn) && connection_wants_to_flush(conn)) {
     /* s == -1 means it's an incomplete edge connection, or that the socket
      * has already been closed as unflushable. */
-    int sz = connection_bucket_write_limit(conn);
+    int sz = connection_bucket_write_limit(conn, now);
     if (!conn->hold_open_until_flushed)
       log_info(LD_NET,
                "Conn (addr %s, fd %d, type %s, state %d) marked, but wants "
@@ -842,6 +852,8 @@
   static time_t time_to_reset_descriptor_failures = 0;
   static time_t time_to_add_entropy = 0;
   static time_t time_to_write_hs_statistics = 0;
+  static time_t time_to_update_hs_dirs = 0;
+  static time_t time_to_perform_replication = 0;
   or_options_t *options = get_options();
   int i;
   int have_dir_info;
@@ -881,16 +893,16 @@
 
   if (time_to_try_getting_descriptors < now) {
     /* XXXX  Maybe we should do this every 10sec when not enough info,
-     * and every 60sec when we have enough info -NM */
+     * and every 60sec when we have enough info -NM Great idea -RD */
     update_router_descriptor_downloads(now);
     update_extrainfo_downloads(now);
+    if (options->UseBridges)
+      fetch_bridge_descriptors(now);
     time_to_try_getting_descriptors = now + DESCRIPTOR_RETRY_INTERVAL;
   }
 
   if (time_to_reset_descriptor_failures < now) {
     router_reset_descriptor_download_failures();
-    if (options->UseBridges)
-      fetch_bridge_descriptors(); /* XXX get this its own retry schedule -RD */
     time_to_reset_descriptor_failures =
       now + DESCRIPTOR_FAILURE_RESET_INTERVAL;
   }
@@ -939,7 +951,8 @@
        * our dirport. not simply if we configured one. -RD */
       if (any_trusted_dir_is_v1_authority() &&
           !should_delay_dir_fetches(options))
-        directory_get_from_dirserver(DIR_PURPOSE_FETCH_DIR, NULL, 1);
+        directory_get_from_dirserver(DIR_PURPOSE_FETCH_DIR,
+                                     ROUTER_PURPOSE_GENERAL, NULL, 1);
     }
 /** How often do we (as a cache) fetch a new V1 directory? */
 #define V1_DIR_FETCH_PERIOD (6*60*60)
@@ -949,18 +962,28 @@
   /* Caches need to fetch running_routers; directory clients don't. */
   if (options->DirPort && time_to_fetch_running_routers < now) {
     if (!authdir_mode_v1(options) && !should_delay_dir_fetches(options)) {
-      directory_get_from_dirserver(DIR_PURPOSE_FETCH_RUNNING_LIST, NULL, 1);
+      directory_get_from_dirserver(DIR_PURPOSE_FETCH_RUNNING_LIST,
+                                   ROUTER_PURPOSE_GENERAL, NULL, 1);
     }
 /** How often do we (as a cache) fetch a new V1 runningrouters document? */
+#ifdef FULL_V1_DIRECTORIES
 #define V1_RUNNINGROUTERS_FETCH_PERIOD (30*60)
+#else
+#define V1_RUNNINGROUTERS_FETCH_PERIOD (6*60*60)
+#endif
     time_to_fetch_running_routers = now + V1_RUNNINGROUTERS_FETCH_PERIOD;
+  }
 
-     /* Also, take this chance to remove old information from rephist
-     * and the rend cache. */
+  /* Once per minute, remove old information from rephist and the rend
+   * cache. */
+  if (time_to_clean_rep_history_and_rend_cache < now) {
     rep_history_clean(now - options->RephistTrackTime);
     rend_cache_clean();
-    /* XXX020 we only clean this stuff if DirPort is set?! -RD */
-  }
+    rend_cache_clean_v2_dir();
+#define REP_HISTORY_AND_REND_CACHE_CLEAN_INTERVAL (60)
+    time_to_clean_rep_history_and_rend_cache = now +
+                                REP_HISTORY_AND_REND_CACHE_CLEAN_INTERVAL;
+  }                                
 
   /* 2b. Once per minute, regenerate and upload the descriptor if the old
    * one is inaccurate. */
@@ -1000,6 +1023,10 @@
     update_networkstatus_downloads(now);
   }
 
+  /** 2c. Let directory voting happen. */
+  if (authdir_mode_v3(options))
+    dirvote_act(now);
+
   /** 3a. Every second, we examine pending circuits and prune the
    *    ones which have been pending for more than a few seconds.
    *    We do this before step 4, so it can try building more if
@@ -1046,7 +1073,7 @@
           buf_shrink(conn->inbuf);
       });
     clean_cell_pool();
-    buf_shrink_freelists();
+    buf_shrink_freelists(0);
     time_to_shrink_memory = now + MEM_SHRINK_INTERVAL;
   }
 
@@ -1084,6 +1111,18 @@
     hs_usage_write_statistics_to_file(now);
     time_to_write_hs_statistics = now+WRITE_HSUSAGE_INTERVAL;
   }
+  
+  /** 11. Update our hidden service routing table. */
+  if (time_to_update_hs_dirs < now) {
+    update_hs_dir_routing_table();
+    time_to_update_hs_dirs = now + UPDATE_HS_DIRS_INTERVAL;
+  }
+  
+  /** 12. Perform replication if we are a hidden service directory. */
+  if (time_to_perform_replication < now) {
+    hs_dir_perform_replication();
+    time_to_perform_replication = now + HS_DIR_REPLICATION_INTERVAL;
+  }
 }
 
 /** Libevent timer: used to invoke second_elapsed_callback() once per
@@ -1133,7 +1172,7 @@
   control_event_stream_bandwidth_used();
 
   if (seconds_elapsed > 0)
-    connection_bucket_refill(seconds_elapsed);
+    connection_bucket_refill(seconds_elapsed, now.tv_sec);
   stats_prev_global_read_bucket = global_read_bucket;
   stats_prev_global_write_bucket = global_write_bucket;
 
@@ -1247,6 +1286,11 @@
 {
   or_options_t *options = get_options();
 
+#ifdef USE_DMALLOC
+  dmalloc_log_stats();
+  dmalloc_log_changed(0, 1, 0, 0);
+#endif
+
   log_notice(LD_GENERAL,"Received reload signal (hup). Reloading config.");
   if (accounting_is_enabled(options))
     accounting_record_bandwidth_usage(time(NULL), get_or_state());
@@ -1532,6 +1576,8 @@
       U64_PRINTF_ARG(rephist_total_alloc), rephist_total_num);
   dump_routerlist_mem_usage(severity);
   dump_cell_pool_usage(severity);
+  buf_dump_freelist_sizes(severity);
+  tor_log_mallinfo(severity);
 }
 
 /** Write all statistics to the log, with log level 'severity'.  Called
@@ -1741,16 +1787,20 @@
 }
 
 /** Free all memory that we might have allocated somewhere.
- * Helps us find the real leaks with dmalloc and the like.
+ * If <b>postfork</b>, we are a worker process and we want to free
+ * only the parts of memory that we won't touch. If !<b>postfork</b>,
+ * Tor is shutting down and we should free everything.
  *
- * Also valgrind should then report 0 reachable in its
- * leak report */
+ * Helps us find the real leaks with dmalloc and the like. Also valgrind
+ * should then report 0 reachable in its leak report (in an ideal world --
+ * in practice libevent, ssl, libc etc never quite free everything). */
 void
 tor_free_all(int postfork)
 {
   if (!postfork) {
     evdns_shutdown(1);
   }
+  dirvote_free_all();
   routerlist_free_all();
   addressmap_free_all();
   set_exit_redirects(NULL); /* free the registered exit redirects */
@@ -1764,6 +1814,7 @@
   circuit_free_all();
   entry_guards_free_all();
   connection_free_all();
+  buf_shrink_freelists(1);
   policies_free_all();
   if (!postfork) {
     config_free_all();
@@ -1772,13 +1823,15 @@
   free_cell_pool();
   tor_tls_free_all();
   /* stuff in main.c */
+  smartlist_free(connection_array);
   smartlist_free(closeable_connection_lst);
   smartlist_free(active_linked_connection_lst);
   tor_free(timeout_event);
   /* Stuff in util.c */
-  escaped(NULL);
   if (!postfork) {
-    close_logs(); /* free log strings. do this last so logs keep working. */
+    escaped(NULL);
+    esc_router_info(NULL);
+    logs_free_all(); /* free log strings. do this last so logs keep working. */
   }
 }
 
@@ -1797,6 +1850,9 @@
     or_state_mark_dirty(get_or_state(), 0); /* force an immediate save. */
     or_state_save(time(NULL));
   }
+#ifdef USE_DMALLOC
+  dmalloc_log_stats();
+#endif
   tor_free_all(0); /* We could move tor_free_all back into the ifdef below
                       later, if it makes shutdown unacceptably slow.  But for
                       now, leave it here: it's helped us catch bugs in the

Modified: tor/branches/114-dist-storage/src/or/or.h
===================================================================
--- tor/branches/114-dist-storage/src/or/or.h	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/or.h	2007-08-11 22:30:44 UTC (rev 11079)
@@ -20,27 +20,27 @@
 #define WIN32_LEAN_AND_MEAN
 #endif
 
-#include <stdio.h>
-#include <stdlib.h>
+// #include <stdio.h>
+// #include <stdlib.h>
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
+//#ifdef HAVE_STRING_H
+//#include <string.h>
+//#endif
 #ifdef HAVE_SIGNAL_H
 #include <signal.h>
 #endif
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
-#ifdef HAVE_CTYPE_H
-#include <ctype.h>
-#endif
+//#ifdef HAVE_CTYPE_H
+//#include <ctype.h>
+//#endif
 #ifdef HAVE_SYS_PARAM_H
 #include <sys/param.h> /* FreeBSD needs this to know what version it is */
 #endif
-#include "../common/torint.h"
+#include "torint.h"
 #ifdef HAVE_SYS_WAIT_H
 #include <sys/wait.h>
 #endif
@@ -53,21 +53,21 @@
 #ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
 #endif
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
+//#ifdef HAVE_SYS_SOCKET_H
+//#include <sys/socket.h>
+//#endif
 #ifdef HAVE_SYS_UN_H
 #include <sys/un.h>
 #endif
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
+//#ifdef HAVE_SYS_TIME_H
+//#include <sys/time.h>
+//#endif
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
+//#ifdef HAVE_NETINET_IN_H
+//#include <netinet/in.h>
+//#endif
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
@@ -91,6 +91,7 @@
 #define MAXCONNECTIONS 15000
 #endif
 
+#if 0
 #ifdef MS_WINDOWS
 /* No, we don't need to redefine FD_SETSIZE before including winsock:
  * we use libevent now, and libevent handles the select() stuff.  Yes,
@@ -107,6 +108,7 @@
 #include <ws2tcpip.h>
 #endif
 #endif
+#endif
 
 #ifdef MS_WINDOWS
 #include <io.h>
@@ -118,13 +120,13 @@
 
 #include <event.h>
 
-#include "../common/crypto.h"
-#include "../common/tortls.h"
-#include "../common/log.h"
-#include "../common/compat.h"
-#include "../common/container.h"
-#include "../common/util.h"
-#include "../common/torgzip.h"
+#include "crypto.h"
+#include "tortls.h"
+#include "log.h"
+#include "compat.h"
+#include "container.h"
+#include "util.h"
+#include "torgzip.h"
 
 /* These signals are defined to help control_signal_act work.
  */
@@ -154,6 +156,12 @@
 #define cell_t tor_cell_t
 #endif
 
+/** Undefine this when it's time to stop generating v1 directories. */
+// #define FULL_V1_DIRECTORIES
+/** Undefine this when it's time to stop includeing bandwidth info in router
+ * descriptors. */
+#define INCLUDE_BW_INFO_IN_ROUTERDESCS
+
 /** Length of longest allowable configured nickname. */
 #define MAX_NICKNAME_LEN 19
 /** Length of a router identity encoded as a hexadecimal digest, plus
@@ -365,15 +373,25 @@
 /** A connection to a directory server: upload a rendezvous
  * descriptor. */
 #define DIR_PURPOSE_UPLOAD_RENDDESC 9
+/** A connection to a directory server: upload a v3 networkstatus vote. */
+#define DIR_PURPOSE_UPLOAD_VOTE 10
+/** A connection to a directory server: fetch a v3 networkstatus vote. */
+#define DIR_PURPOSE_FETCH_VOTE 11
+/** A connection to a directory server: upload a v3 consensus signature */
+#define DIR_PURPOSE_UPLOAD_SIGNATURES 12
+
 /** Purpose for connection at a directory server. */
-#define DIR_PURPOSE_SERVER 10
-/** A connection to a directory server: upload a v2 rendezvous
+#define DIR_PURPOSE_SERVER 13
+/** A connection to a hidden service directory server: upload a v2 rendezvous
  * descriptor. */
-#define DIR_PURPOSE_UPLOAD_RENDDESC_V2 11
-/** A connection to a directory server: download a v2 rendezvous
+#define DIR_PURPOSE_UPLOAD_RENDDESC_V2 14
+/** A connection to a hidden service directory server: download a v2 rendezvous
  * descriptor. */
-#define DIR_PURPOSE_FETCH_RENDDESC_V2 12
-#define _DIR_PURPOSE_MAX 12
+#define DIR_PURPOSE_FETCH_RENDDESC_V2 15
+/** A connection to a hidden service directory server: download replicas for
+ * v2 rendezvous descriptors. */
+#define DIR_PURPOSE_REPLICATE_RENDDESC_V2 16
+#define _DIR_PURPOSE_MAX 16
 
 #define _EXIT_PURPOSE_MIN 1
 /** This exit stream wants to do an ordinary connect. */
@@ -595,17 +613,14 @@
 #define END_CIRC_REASON_NOSUCHSERVICE   12
 #define _END_CIRC_REASON_MAX            12
 
-/* OR this with the argument to circuit_mark_for_close, or
- * control_event_circuit_status to indicate that the reason came from a
- * destroy or truncate cell. */
+/** Bitwise-OR this with the argument to circuit_mark_for_close() or
+ * control_event_circuit_status() to indicate that the reason was
+ * passed through from a destroy or truncate cell. */
 #define END_CIRC_REASON_FLAG_REMOTE     512
 
 /** Length of 'y' portion of 'y.onion' URL. */
 #define REND_SERVICE_ID_LEN 16
 
-/** Length of v2 descriptor ID (32 base32 chars = 160 bits). */
-#define REND_DESC_ID_V2_LEN 32
-
 #define CELL_DIRECTION_IN 1
 #define CELL_DIRECTION_OUT 2
 
@@ -728,6 +743,15 @@
 #define DIR_CONNECTION_MAGIC 0x9988ffeeu
 #define CONTROL_CONNECTION_MAGIC 0x8abc765du
 
+//#define MIN_UPTIME_HS_DIR (24*60*60)
+#define MIN_UPTIME_HS_DIR (30*60)
+#define HS_DIR_REACHABLE_TIMEOUT (45*60)
+#define NUMBER_OF_CONSECUTIVE_REPLICAS (3)
+#define NUMBER_OF_NON_CONSECUTIVE_REPLICAS (2)
+
+/** Length of v2 descriptor ID (32 base32 chars = 160 bits). */
+#define REND_DESC_ID_V2_LEN 32
+
 /** Description of a connection to another host or process, and associated
  * data.
  *
@@ -841,9 +865,9 @@
 
   tor_tls_t *tls; /**< TLS connection state. */
   int tls_error; /**< Last tor_tls error code. */
-  /** Whether we are using this conn for any client traffic. If we're
-   * not, we can rate limit it further. */
-  uint8_t client_used:1;
+  /** When we last used this conn for any client traffic. If not
+   * recent, we can rate limit it further. */
+  time_t client_used;
 
   circ_id_type_t circ_id_type:2; /**< When we send CREATE cells along this
                                   * connection, which half of the space should
@@ -924,6 +948,9 @@
    * already retried several times. */
   uint8_t num_socks_retries;
 
+  /** True iff this connection is for a dns request only. */
+  unsigned int is_dns_request : 1;
+
   /** If this is a DNSPort connection, this field holds the pending DNS
    * request that we're going to try to answer.  */
   struct evdns_server_request *dns_server_request;
@@ -1043,8 +1070,9 @@
 
   /* XXXX020 make this ipv6-capable */
   uint32_t addr; /**< Base address to accept or reject. */
-  uint32_t msk; /**< Accept/reject all addresses <b>a</b> such that
-                 * a &amp; msk == <b>addr</b> &amp; msk . */
+  maskbits_t maskbits; /**< Accept/reject all addresses <b>a</b> such that the
+                 * first <b>maskbits</b> bits of <b>a</b> match
+                 * <b>addr</b>. */
   uint16_t prt_min; /**< Lowest port number to accept/reject. */
   uint16_t prt_max; /**< Highest port number to accept/reject. */
 
@@ -1167,6 +1195,9 @@
   unsigned int is_exit:1; /**< Do we think this is an OK exit? */
   unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked,
                                * or otherwise nasty? */
+  unsigned int wants_to_be_hs_dir:1; /** True iff this router has set a flag
+                                         to possibly act as hidden service
+                                         directory. */
   unsigned int is_hs_dir:1; /** True iff this router is a hidden service
                              * directory. */
 
@@ -1340,8 +1371,8 @@
   char vote_digest[DIGEST_LEN];
   char signing_key_digest[DIGEST_LEN]; /* This part is _not_ signed. */
 
-  char *pending_signature;
-  int pending_signature_len;
+  char *signature;
+  int signature_len;
   unsigned int bad_signature : 1;
   unsigned int good_signature : 1;
 } networkstatus_voter_info_t;
@@ -1371,6 +1402,16 @@
                                    * otherwise just routerstatus_t. */
 } networkstatus_vote_t;
 
+/* XXXX020 merge with networkstatus_vote_t ?? */
+/** DOCDOC */
+typedef struct ns_detached_signatures_t {
+  time_t valid_after;
+  time_t fresh_until;
+  time_t valid_until;
+  char networkstatus_digest[DIGEST_LEN];
+  smartlist_t *signatures; /* list of networkstatus_voter_info_t */
+} ns_detached_signatures_t;
+
 /** Contents of a directory of onion routers. */
 typedef struct {
   /** Map from server identity digest to a member of routers. */
@@ -1396,7 +1437,8 @@
   tor_mmap_t *mmap_descriptors;
   /** Mmaped file holding extra-info documents. */
   tor_mmap_t *mmap_extrainfo;
-  /** List of hidden service directories, sorted by their descriptor IDs. */
+  /** Sorted list of the digests of all onion routers that are serving as
+   * hidden service directories. */
   smartlist_t *hs_dirs;
 } routerlist_t;
 
@@ -1420,6 +1462,7 @@
   signed_descriptor_t cache_info;
   crypto_pk_env_t *identity_key;
   crypto_pk_env_t *signing_key;
+  char signing_key_digest[DIGEST_LEN];
   time_t expires;
 } authority_cert_t;
 
@@ -1765,15 +1808,19 @@
   /* XXXX020 make this whole mess ipv6-capable.  (Does anybody use it? */
 
   uint32_t addr;
-  uint32_t mask;
   uint16_t port_min;
   uint16_t port_max;
+  maskbits_t maskbits;
 
   uint32_t addr_dest;
   uint16_t port_dest;
   unsigned is_redirect:1;
 } exit_redirect_t;
 
+/* limits for TCP send and recv buffer size used for constrained sockets */
+#define MIN_CONSTRAINED_TCP_BUFFER 2048
+#define MAX_CONSTRAINED_TCP_BUFFER 262144  /* 256k */
+
 /** A linked list of lines in a config file. */
 typedef struct config_line_t {
   char *key;
@@ -1918,6 +1965,9 @@
   config_line_t *ReachableORAddresses; /**< IP:ports for OR conns. */
   config_line_t *ReachableDirAddresses; /**< IP:ports for Dir conns. */
 
+  int ConstrainedSockets; /**< Shrink xmit and recv socket buffers. */
+  uint64_t ConstrainedSockSize; /**< Size of constrained buffers. */
+
   /** Application ports that require all nodes in circ to have sufficient
    * uptime. */
   smartlist_t *LongLivedPorts;
@@ -2127,6 +2177,7 @@
 #define SOCKS_COMMAND_RESOLVE       0xF0
 /** Please turn this IP address into an FQDN, privately. */
 #define SOCKS_COMMAND_RESOLVE_PTR   0xF1
+
 /** Please open an encrypted direct TCP connection to the directory port
  * of the Tor server specified by address:port. (In this case address:port
  * specifies the ORPort of the server.) */
@@ -2170,7 +2221,8 @@
 void buf_free(buf_t *buf);
 void buf_clear(buf_t *buf);
 void buf_shrink(buf_t *buf);
-void buf_shrink_freelists(void);
+void buf_shrink_freelists(int free_all);
+void buf_dump_freelist_sizes(int severity);
 
 size_t buf_datalen(const buf_t *buf);
 size_t buf_capacity(const buf_t *buf);
@@ -2252,15 +2304,18 @@
 void entry_guards_update_state(or_state_t *state);
 int getinfo_helper_entry_guards(control_connection_t *conn,
                                 const char *question, char **answer);
-void entry_guards_free_all(void);
 
 void clear_bridge_list(void);
-int routerinfo_is_a_bridge(routerinfo_t *ri);
+int routerinfo_is_a_configured_bridge(routerinfo_t *ri);
 void bridge_add_from_config(uint32_t addr, uint16_t port, char *digest);
-void fetch_bridge_descriptors(void);
+void fetch_bridge_descriptors(time_t now);
 void learned_bridge_descriptor(routerinfo_t *ri);
 int any_bridge_descriptors_known(void);
+int bridges_should_be_retried(void);
+void bridges_retry_all(void);
 
+void entry_guards_free_all(void);
+
 /********************************* circuitlist.c ***********************/
 
 circuit_t * _circuit_get_global_list(void);
@@ -2414,10 +2469,10 @@
 int retry_all_listeners(smartlist_t *replaced_conns,
                         smartlist_t *new_conns);
 
-int connection_bucket_write_limit(connection_t *conn);
+int connection_bucket_write_limit(connection_t *conn, time_t now);
 int global_write_bucket_low(connection_t *conn, size_t attempt, int priority);
 void connection_bucket_init(void);
-void connection_bucket_refill(int seconds_elapsed);
+void connection_bucket_refill(int seconds_elapsed, time_t now);
 
 int connection_handle_read(connection_t *conn);
 
@@ -2496,7 +2551,8 @@
                                             int answer_type,
                                             size_t answer_len,
                                             const char *answer,
-                                            int ttl);
+                                            int ttl,
+                                            time_t expires);
 
 int connection_exit_begin_conn(cell_t *cell, circuit_t *circ);
 int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ);
@@ -2518,7 +2574,7 @@
 void addressmap_clear_configured(void);
 void addressmap_clear_transient(void);
 void addressmap_free_all(void);
-int addressmap_rewrite(char *address, size_t maxlen);
+int addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out);
 int addressmap_have_mapping(const char *address);
 void addressmap_register(const char *address, char *new_address,
                          time_t expires);
@@ -2531,7 +2587,7 @@
 int address_is_in_virtual_range(const char *addr);
 const char *addressmap_register_virtual_address(int type, char *new_address);
 void addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
-                             time_t max_expires);
+                             time_t max_expires, int want_expiry);
 int connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
                                                origin_circuit_t *circ,
                                                crypt_path_t *cpath);
@@ -2645,7 +2701,7 @@
 void control_event_logmsg(int severity, unsigned int domain, const char *msg);
 int control_event_descriptors_changed(smartlist_t *routers);
 int control_event_address_mapped(const char *from, const char *to,
-                                 time_t expires);
+                                 time_t expires, const char *error);
 int control_event_or_authdir_new_descriptor(const char *action,
                                             const char *desc,
                                             size_t desclen,
@@ -2688,10 +2744,11 @@
 /********************************* directory.c ***************************/
 
 char *authority_type_to_string(authority_type_t auth);
-void directory_post_to_dirservers(uint8_t purpose, authority_type_t type,
-                                  const char *payload,
+void directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
+                                  authority_type_t type, const char *payload,
                                   size_t payload_len, size_t extrainfo_len);
-void directory_get_from_dirserver(uint8_t dir_purpose, const char *resource,
+void directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
+                                  const char *resource,
                                   int retry_if_no_servers);
 void directory_initiate_command_routerstatus(routerstatus_t *status,
                                              uint8_t dir_purpose,
@@ -2722,10 +2779,16 @@
                                     int decode_hex, int sort_uniq);
 char *directory_dump_request_log(void);
 int router_supports_extrainfo(const char *identity_digest, int is_authority);
-void directory_post_to_hs_dir(const char *desc_id, const char *payload);
+void directory_post_to_hs_dir(const char *service_id, char desc_ids[][20],
+                              smartlist_t *descs, int seconds_valid);
 void directory_get_from_hs_dir(const char *desc_id, const char *query,
                                const char *secret_cookie);
+void hs_dir_fetch_replicas(routerstatus_t *hs_dir,
+                                        const char *from_id,
+                                        const char *to_id);
 
+#define TIME_PERIOD_LENGTH (24*60*60)
+#define TIME_PERIOD_TWO (60*60)
 /********************************* dirserv.c ***************************/
 
 #define UNNAMED_ROUTER_NICKNAME "Unnamed"
@@ -2752,11 +2815,14 @@
 void directory_set_dirty(void);
 cached_dir_t *dirserv_get_directory(void);
 cached_dir_t *dirserv_get_runningrouters(void);
+cached_dir_t *dirserv_get_consensus(void);
 void dirserv_set_cached_directory(const char *directory, time_t when,
                                   int is_running_routers);
 void dirserv_set_cached_networkstatus_v2(const char *directory,
                                          const char *identity,
                                          time_t published);
+void dirserv_set_cached_networkstatus_v3(const char *consensus,
+                                         time_t published);
 void dirserv_clear_old_networkstatuses(time_t cutoff);
 void dirserv_clear_old_v1_info(time_t now);
 void dirserv_get_networkstatus_v2(smartlist_t *result, const char *key);
@@ -2782,7 +2848,10 @@
                               int first_line_only);
 void dirserv_free_all(void);
 void cached_dir_decref(cached_dir_t *d);
+cached_dir_t *new_cached_dir(char *s, time_t published);
 
+cached_dir_t *generate_v3_networkstatus(void);
+
 #ifdef DIRSERV_PRIVATE
 char *
 format_networkstatus_vote(crypto_pk_env_t *private_key,
@@ -2791,6 +2860,9 @@
 
 /********************************* dirvote.c ************************/
 
+void dirvote_free_all(void);
+
+/* vote manipulation */
 void networkstatus_vote_free(networkstatus_vote_t *ns);
 char *networkstatus_compute_consensus(smartlist_t *votes,
                                       int total_authorities,
@@ -2800,10 +2872,41 @@
                                        networkstatus_vote_t *vote,
                                        const char *identity);
 int networkstatus_check_consensus_signature(networkstatus_vote_t *consensus);
+int networkstatus_add_consensus_signatures(networkstatus_vote_t *target,
+                                           networkstatus_vote_t *src,
+                                           char **new_signatures_out);
+int networkstatus_add_detached_signatures(networkstatus_vote_t *target,
+                                          ns_detached_signatures_t *sigs,
+                                          char **new_signatures_out);
+char *networkstatus_get_detached_signatures(networkstatus_vote_t *consensus);
+void ns_detached_signatures_free(ns_detached_signatures_t *s);
 
+/* cert manipulation */
 void authority_cert_free(authority_cert_t *cert);
 authority_cert_t *authority_cert_dup(authority_cert_t *cert);
 
+/** DOCDOC */
+typedef struct vote_timing_t {
+  int vote_interval;
+  int n_intervals_valid;
+  int vote_delay;
+  int dist_delay;
+} vote_timing_t;
+/* vote scheduling */
+void dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out);
+time_t dirvote_get_start_of_next_interval(time_t now, int interval);
+void dirvote_recalculate_timing(time_t now);
+void dirvote_act(time_t now);
+
+/* invoked on timers and by outside triggers. */
+void dirvote_perform_vote(void);
+void dirvote_clear_pending_votes(void);
+struct pending_vote_t * dirvote_add_vote(const char *vote_body,
+                                         const char **msg_out);
+int dirvote_compute_consensus(void);
+int dirvote_add_signatures(const char *detached_signatures_body);
+int dirvote_publish_consensus(void);
+
 #ifdef DIRVOTE_PRIVATE
 int networkstatus_check_voter_signature(networkstatus_vote_t *consensus,
                                         networkstatus_voter_info_t *voter,
@@ -2835,6 +2938,7 @@
                       const char *answer,
                       int ttl);
 void dnsserv_reject_request(edge_connection_t *conn);
+void dnsserv_launch_request(const char *name, int is_reverse);
 
 /********************************* hibernate.c **********************/
 
@@ -3078,7 +3182,7 @@
                                    size_t request_len);
 void rend_client_desc_here(const char *query);
 
-extend_info_t *rend_client_get_random_intro(const char *query);
+extend_info_t *rend_client_get_random_intro(const char *query, uint8_t version);
 
 int rend_client_send_introduction(origin_circuit_t *introcirc,
                                   origin_circuit_t *rendcirc);
@@ -3088,7 +3192,7 @@
 /** Information used to connect to a hidden service. */
 typedef struct rend_service_descriptor_t {
   crypto_pk_env_t *pk; /**< This service's public key. */
-  int version; /**< 0 to 2. */
+  int version; /**< 0 or 2. */
   time_t timestamp; /**< Time when the descriptor was generated. */
   uint16_t protocols; /**< Bitmask: which rendezvous protocols are supported?
                        * (We allow bits '0', '1', and '2' to be set.) */
@@ -3102,27 +3206,6 @@
    * from this array if introduction attempts fail.  If this array is present,
    * its elements correspond to the elements of intro_points. */
   extend_info_t **intro_point_extend_info;
-  /** Descriptor identifiers (current and next) for distributed storage. */
-  char desc_id[2][32+1];
-  /** Number of seconds that the current desc_id will be valid. */
-  int seconds_valid;
-  /** Permanent part of the computation of desc_id that is derived from pk
-   * or passed by a client as part of the onion address. */
-  char permanent_id[10];
-  /** Changing part (current and next) of the computation of desc_id that is
-   * computed from time_period and secret_cookie. */
-  char secret_id_part[2][32+1];
-  /** Derived value of current and next time period. */
-  char time_period[2][4];
-  /** AES-encrypted list of introduction points. */
-  char *intro_points_encrypted;
-  /** Size of encrypted string. */
-  int intro_points_encrypted_size;
-  /** Secret cookie for generating secret_id_part and {en|de}crypting the
-   * introduction points. Is randomly generated by the hidden service provider
-   * on first configuration of the service and passed by a client as part of
-   * the onion address; storing nodes do not know this and set it to NULL. */
-  char secret_cookie[24+1];
 } rend_service_descriptor_t;
 
 int rend_cmp_service_ids(const char *one, const char *two);
@@ -3132,7 +3215,6 @@
 
 void rend_service_descriptor_free(rend_service_descriptor_t *desc);
 int rend_encode_service_descriptor(rend_service_descriptor_t *desc,
-                                   int version,
                                    crypto_pk_env_t *key,
                                    char **str_out,
                                    size_t *len_out);
@@ -3150,23 +3232,29 @@
 
 void rend_cache_init(void);
 void rend_cache_clean(void);
+void rend_cache_clean_v2_dir(void);
 void rend_cache_free_all(void);
 int rend_valid_service_id(const char *query);
 int rend_cache_lookup_desc(const char *query, int version, const char **desc,
                            size_t *desc_len);
 int rend_cache_lookup_entry(const char *query, int version,
                             rend_cache_entry_t **entry_out);
-int rend_cache_lookup_v2_dir(const char *query, const char **desc,
-                             size_t *desc_len);
+int rend_cache_lookup_v2_dir(const char *query, char **desc);
+int rend_cache_lookup_v2_replicas(const char *query, char **descs);
 int rend_cache_store(const char *desc, size_t desc_len, int published);
 int rend_cache_store_v2_client(const char *desc, const char *secret_cookie);
 int rend_cache_store_v2_dir(const char *desc);
 int rend_cache_size(void);
-int rend_encode_v2_descriptor(char *current_desc, char *next_desc,
-                              rend_service_descriptor_t *desc,
-                              time_t now, const char *secret_cookie);
-int rend_compute_desc_id(char *desc_id, const char *permanent_id,
-                         const char *secret_cookie, time_t now);
+int rend_cache_size_v2_dir(void);
+void print_out_descs_v2_dir(void);
+int rend_encode_v2_descriptors(smartlist_t *desc_strs,
+                               char desc_ids2[][DIGEST_LEN],
+                               rend_service_descriptor_t *desc, time_t now,
+                               const char *secret_cookie, int period);
+void rend_cache_clean_up(void);
+int rend_compute_v2_desc_id(char *desc_id, const char *service_id,
+                         const char *secret_cookie, time_t now, int replica);
+int hs_dir_is_in_interval(const char *a, const char *b, const char *c);
 
 /********************************* rendservice.c ***************************/
 
@@ -3225,6 +3313,7 @@
 int authdir_mode(or_options_t *options);
 int authdir_mode_v1(or_options_t *options);
 int authdir_mode_v2(or_options_t *options);
+int authdir_mode_v3(or_options_t *options);
 int authdir_mode_handles_descs(or_options_t *options);
 int authdir_mode_publishes_statuses(or_options_t *options);
 int authdir_mode_tests_reachability(or_options_t *options);
@@ -3291,7 +3380,7 @@
   /** What kind of authority is this? (Bitfield.) */
   authority_type_t type;
 
-  authority_cert_t *v3_cert; /**< V3 key certificate for this authority */
+  smartlist_t *v3_certs; /**< V3 key certificates for this authority */
 
   int n_networkstatus_failures; /**< How many times have we asked for this
                                  * server's network-status unsuccessfully? */
@@ -3318,6 +3407,8 @@
      const char *digest);
 trusted_dir_server_t *trusteddirserver_get_by_v3_auth_digest(
      const char *digest);
+authority_cert_t *authority_cert_get_by_digests(const char *id_digest,
+                                                const char *sk_digest);
 void routerlist_add_family(smartlist_t *sl, routerinfo_t *router);
 void add_nickname_list_to_smartlist(smartlist_t *sl, const char *list,
                                     int must_be_running);
@@ -3357,8 +3448,7 @@
 void routerlist_reset_warnings(void);
 void routerlist_free(routerlist_t *routerlist);
 void dump_routerlist_mem_usage(int severity);
-void routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int idx,
-                       int make_old);
+void routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int make_old);
 void routerinfo_free(routerinfo_t *router);
 void extrainfo_free(extrainfo_t *extrainfo);
 
@@ -3404,6 +3494,11 @@
 local_routerstatus_t *router_get_combined_status_by_descriptor_digest(
                                                           const char *digest);
 
+/* for consensuses. */
+networkstatus_vote_t *networkstatus_get_latest_consensus(void);
+networkstatus_vote_t *networkstatus_get_live_consensus(time_t now);
+int networkstatus_set_current_consensus(const char *consensus);
+
 //routerstatus_t *routerstatus_get_by_hexdigest(const char *hexdigest);
 int should_delay_dir_fetches(or_options_t *options);
 void update_networkstatus_downloads(time_t now);
@@ -3434,8 +3529,21 @@
 int trusted_dirs_load_certs_from_string(const char *contents, int from_store);
 void trusted_dirs_flush_certs_to_disk(void);
 
-routerstatus_t *get_responsible_hs_dir(const char *id);
+void update_hs_dir_routing_table(void);
+int have_enough_hs_dirs(void);
+int get_responsible_hs_dirs(const char *id, routerstatus_t **hs_dirs);
+void hs_dir_perform_replication(void);
+//smartlist_t *get_hs_dirs(void);
+const char *next_hs_dir(const char *id);
+const char *previous_hs_dir(const char *id);
+int acting_as_hs_dir(void);
+int responsible_for_desc_id(const char *id);
 
+/*int smartlist_digest_is_in_interval(const char *a, const char *b,
+                                    const char *c);
+int smartlist_digest_same_elements(const smartlist_t *sl1,
+                                   const smartlist_t *sl2);*/
+
 /********************************* routerparse.c ************************/
 
 #define MAX_STATUS_TAG_LEN 32
@@ -3517,13 +3625,17 @@
 networkstatus_t *networkstatus_parse_from_string(const char *s);
 networkstatus_vote_t *networkstatus_parse_vote_from_string(const char *s,
                                                            int is_vote);
+ns_detached_signatures_t *networkstatus_parse_detached_signatures(
+                                          const char *s, const char *eos);
 
 authority_cert_t *authority_cert_parse_from_string(const char *s,
                                                    const char **end_of_string);
-int rend_parse_v2_service_descriptor(rend_service_descriptor_t *result,
-                                     const char *str);
+int rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed,
+                                 char *desc_id, char **intro_points_encrypted,
+                                 size_t *intro_points_encrypted_size,
+                                 const char **desc, int eat_desc);
 int rend_decrypt_introduction_points(rend_service_descriptor_t *parsed,
-                                 const char *secret_cookie);
+                                 const char *secret_cookie, char *intro_content, size_t intro_size);
 
 #endif
 

Modified: tor/branches/114-dist-storage/src/or/rendclient.c
===================================================================
--- tor/branches/114-dist-storage/src/or/rendclient.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/rendclient.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -69,7 +69,7 @@
   tor_assert(!rend_cmp_service_ids(introcirc->rend_query,
                                    rendcirc->rend_query));
 
-  if (rend_cache_lookup_entry(introcirc->rend_query, -1, &entry) < 1) {
+  if (rend_cache_lookup_entry(introcirc->rend_query, introcirc->rend_version, &entry) < 1) {
     log_warn(LD_REND,
              "query %s didn't have valid rend desc in cache. Failing.",
              escaped_safe_str(introcirc->rend_query));
@@ -144,6 +144,7 @@
   payload_len = DIGEST_LEN + r;
   tor_assert(payload_len <= RELAY_PAYLOAD_SIZE); /* we overran something */
 
+  log_info(LD_REND, "Sending an INTRODUCE1 cell");
   if (relay_send_command_from_edge(0, TO_CIRCUIT(introcirc),
                                    RELAY_COMMAND_INTRODUCE1,
                                    payload, payload_len,
@@ -227,7 +228,7 @@
        * another intro point and try again. */
       extend_info_t *extend_info;
       int result;
-      extend_info = rend_client_get_random_intro(circ->rend_query);
+      extend_info = rend_client_get_random_intro(circ->rend_query, circ->rend_version);
       if (!extend_info) {
         log_warn(LD_REND, "No introduction points left for %s. Closing.",
                  escaped_safe_str(circ->rend_query));
@@ -257,39 +258,49 @@
 {
   if (!get_options()->FetchHidServDescriptors)
     return;
+  log_info(LD_REND, "Fetching rendezvous descriptor for service %s", query);
   if (connection_get_by_type_state_rendquery(CONN_TYPE_DIR, 0, query)) {
     log_info(LD_REND,"Would fetch a new renddesc here (for %s), but one is "
              "already in progress.", escaped_safe_str(query));
   } else {
     /* not one already; initiate a dir rend desc lookup */
-    directory_get_from_dirserver(DIR_PURPOSE_FETCH_RENDDESC, query, 1);
+    directory_get_from_dirserver(DIR_PURPOSE_FETCH_RENDDESC,
+                                 ROUTER_PURPOSE_GENERAL, query, 1);
   }
 }
 
-/** If we are not currently fetching a rendezvous service descriptor
- * for the service ID <b>query</b>, start a directory connection to fetch a
- * new one.
- *
- * query = 16 base32 chars, secret_cookie = 24 base32 chars
+/** If we are not currently fetching a rendezvous service descriptor for the
+ * base32-encoded service ID <b>query</b> and base32-encoded
+ * <b>secret_cookie</b>, start a connection to a hidden service directory to
+ * fetch a new one.
  */
 void
 rend_client_refetch_v2_renddesc(const char *query, const char *secret_cookie)
 {
+  char descriptor_id[DIGEST_LEN];
+  int replica;
   tor_assert(query);
   tor_assert(strlen(query) == 16);
   tor_assert(secret_cookie);
   tor_assert(strlen(secret_cookie) == 24);
-
+  /* Are we configured to fetch v2 descriptors? */
   if (!get_options()->FetchV2HidServDescriptors) {
-    log_warn(LD_DIR, "We received an onion address for a v2 service "
-        "descriptor, but fetching v2 service descriptors is not configured. "
-        "You need to set the config option FetchV2HidServDescriptors in order "
-        "to perform this request!");
+    log_warn(LD_REND, "We received an onion address for a v2 rendezvous "
+        "service descriptor, but fetching v2 service descriptors is not "
+        "configured. You need to set the config option "
+        "FetchV2HidServDescriptors in order to perform this request!");
     return;
   }
-  char descriptor_id[32+1];
-  rend_compute_desc_id(descriptor_id, query,
-                       secret_cookie, time(NULL));
+  /* Are we aware of enough hidden service directories? */
+  if (!have_enough_hs_dirs()) {
+    log_warn(LD_REND, "We don't have enough hidden service directories to "
+                     "fetch v2 descriptors!");
+    return;
+  }
+  log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s", query);
+  replica = crypto_rand_int(NUMBER_OF_NON_CONSECUTIVE_REPLICAS);
+  rend_compute_v2_desc_id(descriptor_id, query,
+                       secret_cookie, time(NULL), replica);
   directory_get_from_hs_dir(descriptor_id, query, secret_cookie);
   return;
 }
@@ -308,7 +319,7 @@
   rend_cache_entry_t *ent;
   connection_t *conn;
 
-  r = rend_cache_lookup_entry(query, -1, &ent);
+  r = rend_cache_lookup_entry(query, rend_version, &ent);
   if (r<0) {
     log_warn(LD_BUG, "Malformed service ID %s.", escaped_safe_str(query));
     return -1;
@@ -418,6 +429,8 @@
     goto err;
   }
 
+  log_info(LD_REND,"Got RENDEZVOUS2 cell from hidden service.");
+
   /* first DH_KEY_LEN bytes are g^y from bob. Finish the dh handshake...*/
   tor_assert(circ->build_state);
   tor_assert(circ->build_state->pending_final_cpath);
@@ -481,7 +494,7 @@
     if (rend_cmp_service_ids(query, conn->rend_query))
       continue;
     assert_connection_ok(TO_CONN(conn), now);
-    if (rend_cache_lookup_entry(conn->rend_query, -1, &entry) == 1 &&
+    if (rend_cache_lookup_entry(conn->rend_query, conn->rend_version, &entry) == 1 &&
         entry->parsed->n_intro_points > 0) {
       /* either this fetch worked, or it failed but there was a
        * valid entry from before which we should reuse */
@@ -512,12 +525,12 @@
  * have been tried and failed.
  */
 extend_info_t *
-rend_client_get_random_intro(const char *query)
+rend_client_get_random_intro(const char *query, uint8_t version)
 {
   int i;
   rend_cache_entry_t *entry;
 
-  if (rend_cache_lookup_entry(query, -1, &entry) < 1) {
+  if (rend_cache_lookup_entry(query, version, &entry) < 1) {
     log_warn(LD_REND,
              "Query '%s' didn't have valid rend desc in cache. Failing.",
              safe_str(query));

Modified: tor/branches/114-dist-storage/src/or/rendcommon.c
===================================================================
--- tor/branches/114-dist-storage/src/or/rendcommon.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/rendcommon.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -43,84 +43,13 @@
   tor_free(desc);
 }
 
-#define TIME_PERIOD_LENGTH 600
-
-/* Fill the fields of <b>desc</b> so that it a valid v2 descriptor can be
- * encoded from it. Use the current time <b>now</b> and the
- * <b>secret_cookie</b>. Returns 0 on success, -1 otherwise. */
-static int
-rend_compute_v2_descriptor_fields(rend_service_descriptor_t *desc,
-                                  time_t now, const char *secret_cookie)
-{
-  char permanent_id_temp[DIGEST_LEN];
-  double time_period_part1;
-  double time_period_part2;
-  uint64_t permanent_id_value = 0;
-  uint32_t time_period;
-  int i;
-  crypto_digest_env_t *digest;
-  char desc_id_binary[20];
-  char secret_id_part_bytes[20];
-
-  /* obtain permanent-id from public key */
-  crypto_pk_get_digest(desc->pk, permanent_id_temp);
-  memcpy(desc->permanent_id, permanent_id_temp, 10);
-
-  /* secret_cookie */
-  strlcpy(desc->secret_cookie, secret_cookie, sizeof(desc->secret_cookie));
-  char secret_cookie_bin[16];
-  base32_decode(secret_cookie_bin, sizeof(secret_cookie_bin),
-                desc->secret_cookie, strlen(desc->secret_cookie));
-  secret_cookie_bin[15] = 0;
-
-  /* calculate current time-period */
-  time_period_part1 = ((double) now) / ((double) TIME_PERIOD_LENGTH);
-  permanent_id_value=((((uint64_t) desc->permanent_id[0]) + 256) % 256) << 32;
-  permanent_id_value+=((((uint64_t) desc->permanent_id[1]) + 256) % 256) << 24;
-  permanent_id_value+=((((uint64_t) desc->permanent_id[2]) + 256) % 256) << 16;
-  permanent_id_value+=((((uint64_t) desc->permanent_id[3]) + 256) % 256) << 8;
-  permanent_id_value+=(((uint64_t) desc->permanent_id[4]) + 256) % 256;
-  time_period_part2 = ((double) permanent_id_value) /
-                              ((double) (((uint64_t) 1) << 40));
-  time_period = (uint32_t) (time_period_part1 + time_period_part2);
-  desc->seconds_valid = TIME_PERIOD_LENGTH - (uint32_t) (((time_period_part1
-          + time_period_part2) - (double) time_period) * TIME_PERIOD_LENGTH);
-
-  /* Compute descriptor ids for the current and the next time period */
-  for (i = 0; i < 2; i++) {
-
-    /* time-period */
-    desc->time_period[i][0] = (char) ((time_period+i) >> 24);
-    desc->time_period[i][1] = (char) ((time_period+i) >> 16);
-    desc->time_period[i][2] = (char) ((time_period+i) >> 8);
-    desc->time_period[i][3] = (char) (time_period+i);
-
-    /* secret-id-part = h(time-period + cookie) */
-    digest = crypto_new_digest_env();
-    crypto_digest_add_bytes(digest, desc->time_period[i], 4);
-    crypto_digest_add_bytes(digest, secret_cookie_bin, 16);
-    crypto_digest_get_digest(digest, secret_id_part_bytes, 20);
-    crypto_free_digest_env(digest);
-
-    /* descriptor id */
-    digest = crypto_new_digest_env();
-    crypto_digest_add_bytes(digest, desc->permanent_id, 10);
-    crypto_digest_add_bytes(digest, secret_id_part_bytes, 20);
-    crypto_digest_get_digest(digest, desc_id_binary, 20);
-    crypto_free_digest_env(digest);
-
-    base32_encode(desc->secret_id_part[i], 32+1, secret_id_part_bytes, 20);
-    base32_encode(desc->desc_id[i], 32+1, desc_id_binary, 20);
-  }
-
-  return 0;
-}
-
-/* Compute the <b>desc_id</b> for a given <b>service_id</b> and
- * <b>secret_cookie</b> at time <b>now</b>. */
+/* Compute the <b>desc_id</b> for a given base32-encoded <b>service_id</b> and
+ * base32-encoded <b>secret_cookie</b> at time <b>now</b> for replica number
+ * <b>replica</b>.
+ */
 int
-rend_compute_desc_id(char *desc_id, const char *service_id,
-                     const char *secret_cookie, time_t now)
+rend_compute_v2_desc_id(char *desc_id, const char *service_id,
+                     const char *secret_cookie, time_t now, int replica)
 {
   uint64_t permanent_id_value = 0;
   double time_period_part1;
@@ -129,225 +58,281 @@
   uint32_t time_period;
   crypto_digest_env_t *digest;
   char secret_id_part_digest[20];
-  char service_id_binary[10]; /* service id part of address in binary format */
-  char secret_cookie_binary[16]; /* cookie part of address in binary format;
-                                    the 16th byte is by definition 0. */
-  char descriptor_id_binary[20]; /* computed descriptor id in binary format */
-  //char descriptor_id[32+1];
-  /* check assertions */
+  char service_id_binary[10];
+  char secret_cookie_binary[16];
+  char replica_byte[1];
   tor_assert(service_id);
   tor_assert(strlen(service_id) == 16);
   tor_assert(secret_cookie);
   tor_assert(strlen(secret_cookie) == 24);
-  /* decode base32 strings to binary strings */
+  tor_assert(replica >= 0 && replica < NUMBER_OF_NON_CONSECUTIVE_REPLICAS);
+  /* Convert service ID and secret cookie to binary. */
   base32_decode(service_id_binary, 10, service_id, 16);
   base32_decode(secret_cookie_binary, 15, secret_cookie, 24);
   secret_cookie_binary[15] = 0;
-  /* time-period */
+  /* Calculate current time-period. */
   time_period_part1 = ((double) now) / ((double) TIME_PERIOD_LENGTH);
-  permanent_id_value=((((uint64_t) service_id[0]) + 256) % 256) << 32;
-  permanent_id_value+=((((uint64_t) service_id[1]) + 256) % 256) << 24;
-  permanent_id_value+=((((uint64_t) service_id[2]) + 256) % 256) << 16;
-  permanent_id_value+=((((uint64_t) service_id[3]) + 256) % 256) << 8;
-  permanent_id_value+=(((uint64_t) service_id[4]) + 256) % 256;
+  permanent_id_value = ((((uint64_t) service_id_binary[0]) + 256) % 256) << 32;
+  permanent_id_value +=((((uint64_t) service_id_binary[1]) + 256) % 256) << 24;
+  permanent_id_value +=((((uint64_t) service_id_binary[2]) + 256) % 256) << 16;
+  permanent_id_value +=((((uint64_t) service_id_binary[3]) + 256) % 256) << 8;
+  permanent_id_value += (((uint64_t) service_id_binary[4]) + 256) % 256;
   time_period_part2 = ((double) permanent_id_value) /
                               ((double) (((uint64_t) 1) << 40));
   time_period = (uint32_t) (time_period_part1 + time_period_part2);
+  /* Convert time-period to binary. */
   time_period_bytes[0] = (char) (time_period >> 24);
   time_period_bytes[1] = (char) (time_period >> 16);
   time_period_bytes[2] = (char) (time_period >> 8);
   time_period_bytes[3] = (char) time_period;
-  /* secret-id-part = h(time-period + cookie) */
+  /* Give this replica a number. */
+  replica_byte[0] = (char) replica;
+  /* Calculate secret-id-part = h(time-period + cookie + replica). */
   digest = crypto_new_digest_env();
   crypto_digest_add_bytes(digest, time_period_bytes, 4);
   crypto_digest_add_bytes(digest, secret_cookie_binary, 16);
+  crypto_digest_add_bytes(digest, replica_byte, 1);
   crypto_digest_get_digest(digest, secret_id_part_digest, 20);
   crypto_free_digest_env(digest);
-  /* descriptor id */
+  /* Calculate descriptor ID. */
   digest = crypto_new_digest_env();
   crypto_digest_add_bytes(digest, service_id_binary, 10);
   crypto_digest_add_bytes(digest, secret_id_part_digest, 20);
-  crypto_digest_get_digest(digest, descriptor_id_binary, 20);
+  crypto_digest_get_digest(digest, desc_id, 20);
   crypto_free_digest_env(digest);
-  /* encode binary string to base32 string */
-  base32_encode(desc_id, 32+1, descriptor_id_binary, 20);
   return 0;
 }
 
-/* Encodes the current (<b>current_desc</b>) and next descriptor
- * (<b>next_desc</b>) from <b>desc</b>. Returns 0 on success, -1 otherwise. */
+/** Encode a set of new service descriptors for <b>desc</b> at time <b>now</b>
+ * using <b>secret_cookie</b> for period <b>period</b> (e.g. 0 for the current
+ * period, 1 for the next period, etc.) and write the ASCII-encoded output to
+ * <b>desc_str</b> and the descriptor IDs to <b>desc_ids</b>; return the number
+ * of seconds that this descriptor will be found under that <b>desc_id</b> by
+ * clients, or -1 if the encoding was not successful. */
 int
-rend_encode_v2_descriptor(char *current_desc, char *next_desc,
-                          rend_service_descriptor_t *desc, time_t now,
-                          const char *secret_cookie)
+rend_encode_v2_descriptors(smartlist_t *desc_strs,
+                           char desc_ids[][DIGEST_LEN],
+                           rend_service_descriptor_t *desc, time_t now,
+                           const char *secret_cookie, int period)
 {
-
-  char *pkey; /* public key, PEM-encoded. */
-  size_t pkeylen; /* public key length */
-  char published[ISO_TIME_LEN+1]; /* timestamp */
-  size_t iposlen; /* max length of unencrypted introduction points */
-  char *ipos; /* unencrypted introduction points */
-  int ipowritten; /* actual length of unencrypted introduction points */
-  //char secret_id_part_base32[32+1];
-  //char descriptor_id_base32[32+1];
-  int i;
+  char *pkey;
+  size_t pkeylen;
+  char published[ISO_TIME_LEN+1];
+  size_t iposlen;
+  char *ipos;
+  int ipowritten;
   char *ipos_enc;
   int enclen;
   char *ipos_encrypted_base64;
   int result = 0;
   size_t written = 0;
   char desc_digest[DIGEST_LEN];
-  char *buf;
-  /* check assertions */
+  char permanent_id_temp[DIGEST_LEN];
+  char permanent_id[10];
+  double time_period_part1;
+  double time_period_part2;
+  uint64_t permanent_id_value = 0;
+  uint32_t time_period;
+  crypto_digest_env_t *digest;
+  char desc_id_base32[REND_DESC_ID_V2_LEN+1];
+  char secret_id_part_bytes[20];
+  char time_period_bytes[4];
+  char secret_id_part[32+1];
+  int seconds_valid;
+  int j, k;
+  rend_service_descriptor_t *test_parsed;
+  char test_desc_id[DIGEST_LEN];
+  char *test_intro_content;
+  size_t test_intro_size;
+  char *desc_str;
+  char *desc_id;
+  char secret_cookie_bin[16];
+  char replica_byte[1];
+  size_t desc_len;
   tor_assert(desc);
   tor_assert(secret_cookie);
   tor_assert(strlen(secret_cookie) == 24);
-
-  /* prepare descriptor */
-  rend_compute_v2_descriptor_fields(desc, now, secret_cookie);
-
-  /* PEM-encode the public key */
-  if (crypto_pk_write_public_key_to_string(desc->pk, &pkey, &pkeylen) < 0)
-    return -1;
-
-  /* encode timestamp */
-  format_iso_time(published, desc->timestamp);
-
-  /* assemble unencrypted list of introduction points */
-  iposlen = desc->n_intro_points * 700;
-  ipos = tor_malloc_zero(iposlen);
-  ipowritten = 0;
-  for (i=0; i < desc->n_intro_points; ++i) {
-    char id_base32[32+1];
-    char *okey; /* onion key, PEM-encoded. */
-    size_t okeylen;
-    char *skey; /* service key, PEM_encoded. */
-    size_t skeylen;
-    int res;
-
-    /* obtain extend info with introduction point details */
-    extend_info_t *info = desc->intro_point_extend_info[i];
-
-    /* encode introduction point ID */
-    base32_encode(id_base32, 32+1, info->identity_digest, DIGEST_LEN);
-
-    /* encode onion key */
-    if (crypto_pk_write_public_key_to_string(info->onion_key, &okey,
-                                             &okeylen) < 0) {
-      log_warn(LD_DIR, "write onion key failed");
-      continue;
+  for (k = 0; k < NUMBER_OF_NON_CONSECUTIVE_REPLICAS; k++) {
+    desc_id = *(desc_ids + k);
+    /* Obtain permanent-id from public key. */
+    crypto_pk_get_digest(desc->pk, permanent_id_temp);
+    memcpy(permanent_id, permanent_id_temp, 10);
+    /* Convert secret_cookie to binary. */
+    base32_decode(secret_cookie_bin, sizeof(secret_cookie_bin), secret_cookie,
+                  strlen(secret_cookie));
+    secret_cookie_bin[15] = 0;
+    /* Calculate current time-period. */
+    time_period_part1 = ((double) now) / ((double) TIME_PERIOD_LENGTH);
+    permanent_id_value = ((((uint64_t) permanent_id[0]) + 256) % 256) << 32;
+    permanent_id_value +=((((uint64_t) permanent_id[1]) + 256) % 256) << 24;
+    permanent_id_value +=((((uint64_t) permanent_id[2]) + 256) % 256) << 16;
+    permanent_id_value +=((((uint64_t) permanent_id[3]) + 256) % 256) << 8;
+    permanent_id_value += (((uint64_t) permanent_id[4]) + 256) % 256;
+    time_period_part2 = ((double) permanent_id_value) /
+                                ((double) (((uint64_t) 1) << 40));
+    time_period = (uint32_t) (time_period_part1 + time_period_part2);
+    time_period += period;
+    seconds_valid = TIME_PERIOD_LENGTH
+                    - (uint32_t) (((time_period_part1 + time_period_part2) -
+                                   (double) time_period)
+                                  * TIME_PERIOD_LENGTH);
+    /* Convert time-period to binary. */
+    time_period_bytes[0] = (char) ((time_period) >> 24);
+    time_period_bytes[1] = (char) ((time_period) >> 16);
+    time_period_bytes[2] = (char) ((time_period) >> 8);
+    time_period_bytes[3] = (char) (time_period);
+    /* Give this replica a number. */
+    replica_byte[0] = (char) k;
+    /* Calculate secret-id-part = h(time-period + cookie + replica). */
+    digest = crypto_new_digest_env();
+    crypto_digest_add_bytes(digest, time_period_bytes, 4);
+    crypto_digest_add_bytes(digest, secret_cookie_bin, 16);
+    crypto_digest_add_bytes(digest, replica_byte, 1);
+    crypto_digest_get_digest(digest, secret_id_part_bytes, 20);
+    crypto_free_digest_env(digest);
+    base32_encode(secret_id_part, 32 + 1, secret_id_part_bytes, 20);
+    /* Calculate descriptor ID. */
+    digest = crypto_new_digest_env();
+    crypto_digest_add_bytes(digest, permanent_id, 10);
+    crypto_digest_add_bytes(digest, secret_id_part_bytes, 20);
+    crypto_digest_get_digest(digest, desc_id, 20);
+    crypto_free_digest_env(digest);
+    base32_encode(desc_id_base32, 32 + 1, desc_id, 20);
+    /* PEM-encode the public key */
+    if (crypto_pk_write_public_key_to_string(desc->pk, &pkey, &pkeylen) < 0) {
+      log_warn(LD_REND, "Could not write public key to string.");
+      return -1;
     }
-
-    /* encode service key; TODO replace Bob's public key by newly generated
-     * service key */
-    if (crypto_pk_write_public_key_to_string(desc->pk, &skey, &skeylen) < 0) {
-      log_warn(LD_DIR, "write service key failed");
-      continue;
+    /* Encode timestamp. */
+    format_iso_time(published, desc->timestamp);
+    /* Assemble unencrypted list of introduction points. */
+    iposlen = desc->n_intro_points * 1000; /* too long, but ok. */
+    ipos = tor_malloc_zero(iposlen);
+    ipowritten = 0;
+    for (j=0; j < desc->n_intro_points; ++j) {
+      char id_base32[32 + 1];
+      char *okey;
+      size_t okeylen;
+      char *skey;
+      size_t skeylen;
+      int res;
+      /* Obtain extend info with introduction point details. */
+      extend_info_t *info = desc->intro_point_extend_info[j];
+      /* Encode introduction point ID. */
+      base32_encode(id_base32, 32 + 1, info->identity_digest, DIGEST_LEN);
+      /* Encode onion key. */
+      if (crypto_pk_write_public_key_to_string(info->onion_key, &okey,
+                                               &okeylen) < 0) {
+        log_warn(LD_REND, "Could not write onion key.");
+        continue;
+      }
+      /* Encode service key.
+       * TODO114 replace Bob's public key by newly generated service key */
+      if (crypto_pk_write_public_key_to_string(desc->pk, &skey, &skeylen) < 0) {
+        log_warn(LD_REND, "Could not write service key.");
+        continue;
+      }
+      /* Assemble everything for this introduction point. */
+      res = tor_snprintf(ipos+ipowritten, iposlen-ipowritten,
+                           "introduction-point %s\n"
+                           "ip-address %u.%u.%u.%u\n"
+                           "onion-port %d\n"
+                           "onion-key\n%s"
+                           "service-key\n%s",
+                         id_base32,
+                         (info->addr >> 24) % 256,
+                         (info->addr >> 16) % 256,
+                         (info->addr >> 8) % 256,
+                         info->addr % 256,
+                         info->port,
+                         okey,
+                         skey);
+      if (res < 0) {
+        log_warn(LD_BUG, "Not enough space for writing ipo.");
+        return -1;
+      }
+      /* Update total number of written bytes for unencrypted intro points. */
+      ipowritten += res;
+      /* Free memory. */
+      tor_free(okey);
+      tor_free(skey);
     }
-
-    /* assemble everything for this introduction point */
-    res = tor_snprintf(ipos+ipowritten, iposlen-ipowritten,
-                       "introduction-point %s\n"
-                       "ip-address %u.%u.%u.%u\n"
-                       "onion-port %d\n"
-                       "onion-key\n%s"
-                       "service-key\n%s",
-      id_base32,
-      (info->addr >> 24) % 256,
-      (info->addr >> 16) % 256,
-      (info->addr >> 8) % 256,
-      info->addr % 256,
-      info->port,
-      okey,
-      skey);
-    if (res < 0) {
-      log_warn(LD_BUG, "not enough space for writing ipo");
+    /* Finalize unencrypted introduction points. */
+    ipos[ipowritten++] = '\n';
+    ipos[ipowritten++] = 0;
+    /* Encrypt introduction points. */
+    ipos_enc = tor_malloc_zero(ipowritten + 32);
+    enclen = crypto_cipher_encrypt_cbc(secret_cookie_bin, ipos_enc,
+                                       ipos, ipowritten);
+    /* Free memory for unencrypted introduction points. */
+    tor_free(ipos);
+    /* Base64-encode introduction points. */
+    ipos_encrypted_base64 = tor_malloc_zero(ipowritten * 2);
+    if (base64_encode(ipos_encrypted_base64, ipowritten * 2, ipos_enc,
+                      enclen) < 0) {
+      log_warn(LD_REND, "Could not encode ipos to base64.");
       return -1;
     }
-
-    /* update total number of written bytes for unencrypted intro points */
-    ipowritten += res;
-  }
-
-  /* finalize unencrypted introduction points */
-  ipos[ipowritten++] = '\n';
-  ipos[ipowritten++] = 0;
-
-  /* encrypt introduction points */
-  ipos_enc = tor_malloc_zero(ipowritten + 32);
-  char secret_cookie_bin[16];
-  base32_decode(secret_cookie_bin, sizeof(secret_cookie_bin),
-                desc->secret_cookie, strlen(desc->secret_cookie));
-  secret_cookie_bin[15] = 0;
-  enclen = crypto_cipher_encrypt_cbc(secret_cookie_bin, ipos_enc,
-                                     ipos, ipowritten);
-
-  /* free memory for unencrypted introduction points */
-  tor_free(ipos);
-
-  /* base64-encode introduction points */
-  ipos_encrypted_base64 = tor_malloc_zero(ipowritten * 2);
-  if (base64_encode(ipos_encrypted_base64, ipowritten * 2, ipos_enc,
-                    enclen) < 0) {
-    log_warn(LD_DIR, "could not encode ipos to base64");
-    return -1;
-  }
-
-  /* encode both, the current and the next descriptor */
-  for (i = 0; i < 2; i++) {
-    buf = (i == 0 ? current_desc : next_desc);
-    //base32_encode(secret_id_part_base32, 32+1, desc->secret_id_part[i], 20);
-    //base32_encode(descriptor_id_base32, 32+1, desc->desc_id[i], 20);
-
-    /* complete descriptor; TODO114 make 4000 a little more dynamic */
-    result = tor_snprintf(buf, 4000,
-           "rendezvous-service-descriptor %s\n"
-           "version 2\n"
-           "permanent-key\n%s"
-           "secret-id-part %s\n"
-           "publication-time %s\n"
-           "protocol-versions %d\n"
-           "introduction-points\n-----BEGIN AES ENCRYPTED MESSAGE-----\n%s"
-           "-----END AES ENCRYPTED MESSAGE-----\n",
-      desc->desc_id[i],
-      pkey,
-      desc->secret_id_part[i],
-      published,
-      desc->protocols,
-      ipos_encrypted_base64);
+    /* Assemble complete descriptor. */
+    desc_len = 2000 + desc->n_intro_points * 1000; /* far too long, but ok. */
+    desc_str = tor_malloc_zero(desc_len);
+    result = tor_snprintf(desc_str, desc_len,
+             "rendezvous-service-descriptor %s\n"
+             "version 2\n"
+             "permanent-key\n%s"
+             "secret-id-part %s\n"
+             "publication-time %s\n"
+             "protocol-versions %d\n"
+             "introduction-points\n-----BEGIN AES ENCRYPTED MESSAGE-----\n%s"
+             "-----END AES ENCRYPTED MESSAGE-----\n",
+        desc_id_base32,
+        pkey,
+        secret_id_part,
+        published,
+        desc->protocols,
+        ipos_encrypted_base64);
     if (result < 0) {
-      log_warn(LD_BUG, "descriptor ran out of room!");
+      log_warn(LD_REND, "Descriptor ran out of room.");
       return -1;
     }
     written = result;
-
-    /* add signature */
-    strlcpy(buf + written, "signature\n", 4000 - written);
-    written += strlen(buf + written);
-    buf[written] = '\0';
-    if (crypto_digest(desc_digest, buf, written) < 0) {
-      log_warn(LD_BUG, "could not create digest");
+    /* Add signature. */
+    strlcpy(desc_str + written, "signature\n", desc_len - written);
+    written += strlen(desc_str + written);
+    desc_str[written] = '\0';
+    if (crypto_digest(desc_digest, desc_str, written) < 0) {
+        log_warn(LD_BUG, "could not create digest.");
       return -1;
     }
-    if (router_append_dirobj_signature(buf+written,4000-written,
-                                       desc_digest,desc->pk) < 0) {
-      log_warn(LD_BUG, "Couldn't sign desc");
+    if (router_append_dirobj_signature(desc_str + written, desc_len - written,
+                                       desc_digest, desc->pk) < 0) {
+      log_warn(LD_BUG, "Couldn't sign desc.");
       return -1;
     }
-    written += strlen(buf+written);
-    if (written+2 > 4000) {
-      log_warn(LD_BUG, "could not finish desc");
+    written += strlen(desc_str+written);
+    if (written+2 > desc_len) {
+        log_warn(LD_BUG, "Could not finish desc.");
       return -1;
     }
-    buf[written++] = '\n';
-    buf[written++] = 0;
+    desc_str[written++] = '\n';
+    desc_str[written++] = 0;
+    /* Free memory. */
+    tor_free(pkey);
+    /* Check if we can parse our own descriptor. */
+    if (rend_parse_v2_service_descriptor(&test_parsed, test_desc_id,
+                                         &test_intro_content, &test_intro_size,
+                                         (const char **)(&desc_str), 0) < 0) {
+      log_warn(LD_REND, "Could not parse my own descriptor.");
+      return -1;
+    }
+    smartlist_add(desc_strs, desc_str);
+    tor_free(test_parsed);
+    tor_free(test_intro_content);
   }
 
-  /* free memory */
-  tor_free(pkey);
-
-  /* return descriptor size (both descriptors have the same size) */
-  return written;
+  log_info(LD_REND, "Successfully encoded a v2 descriptor and "
+                    "confirmed that it is parsable.");
+  return seconds_valid;
 }
 
 /** Encode a service descriptor for <b>desc</b>, and sign it with
@@ -356,7 +341,6 @@
  */
 int
 rend_encode_service_descriptor(rend_service_descriptor_t *desc,
-                               int version,
                                crypto_pk_env_t *key,
                                char **str_out, size_t *len_out)
 {
@@ -367,42 +351,17 @@
   size_t buflen = PK_BYTES*2*(desc->n_intro_points+2);/*Too long, but ok*/
   cp = *str_out = tor_malloc(buflen);
   end = cp + PK_BYTES*2*(desc->n_intro_points+1);
-  if (version) {
-    *(uint8_t*)cp = (uint8_t)0xff;
-    *(uint8_t*)(cp+1) = (uint8_t)version;
-    cp += 2;
-  }
   asn1len = crypto_pk_asn1_encode(desc->pk, cp+2, end-(cp+2));
   set_uint16(cp, htons((uint16_t)asn1len));
   cp += 2+asn1len;
   set_uint32(cp, htonl((uint32_t)desc->timestamp));
   cp += 4;
-  if (version == 1) {
-    set_uint16(cp, htons(desc->protocols));
-    cp += 2;
-  }
   set_uint16(cp, htons((uint16_t)desc->n_intro_points));
   cp += 2;
-  if (version == 0) {
-    for (i=0; i < desc->n_intro_points; ++i) {
-      char *ipoint = (char*)desc->intro_points[i];
-      strlcpy(cp, ipoint, buflen-(cp-*str_out));
-      cp += strlen(ipoint)+1;
-    }
-  } else {
-    if (desc->n_intro_points)
-      tor_assert(desc->intro_point_extend_info);
-    for (i=0; i < desc->n_intro_points; ++i) {
-      extend_info_t *info = desc->intro_point_extend_info[i];
-      int klen;
-      set_uint32(cp, htonl(info->addr));
-      set_uint16(cp+4, htons(info->port));
-      memcpy(cp+6, info->identity_digest, DIGEST_LEN);
-      klen = crypto_pk_asn1_encode(info->onion_key, cp+6+DIGEST_LEN+2,
-                                   (end-(cp+6+DIGEST_LEN+2)));
-      set_uint16(cp+6+DIGEST_LEN, htons((uint16_t)klen));
-      cp += 6+DIGEST_LEN+2+klen;
-    }
+  for (i=0; i < desc->n_intro_points; ++i) {
+    char *ipoint = (char*)desc->intro_points[i];
+    strlcpy(cp, ipoint, buflen-(cp-*str_out));
+    cp += strlen(ipoint)+1;
   }
   note_crypto_pk_op(REND_SERVER);
   i = crypto_pk_private_sign_digest(key, cp, *str_out, cp-*str_out);
@@ -432,12 +391,7 @@
   cp = str;
   end = str+len;
   if (end-cp<2) goto truncated;
-  if (*(uint8_t*)cp == 0xff) {
-    result->version = version = *(uint8_t*)(cp+1);
-    cp += 2;
-  } else {
-    result->version = version = 0;
-  }
+  result->version = version = 0;
   if (end-cp < 2) goto truncated;
   asn1len = ntohs(get_uint16(cp));
   cp += 2;
@@ -448,18 +402,12 @@
   if (end-cp < 4) goto truncated;
   result->timestamp = (time_t) ntohl(get_uint32(cp));
   cp += 4;
-  if (version == 1) {
-    if (end-cp < 2) goto truncated;
-    result->protocols = ntohs(get_uint16(cp));
-    cp += 2;
-  } else {
-    result->protocols = 1;
-  }
+  result->protocols = 1;
   if (end-cp < 2) goto truncated;
   result->n_intro_points = ntohs(get_uint16(cp));
   cp += 2;
 
-  if (version == 0 && result->n_intro_points != 0) {
+  if (result->n_intro_points != 0) {
     result->intro_points =
       tor_malloc_zero(sizeof(char*)*result->n_intro_points);
     for (i=0;i<result->n_intro_points;++i) {
@@ -469,33 +417,6 @@
       result->intro_points[i] = tor_strdup(cp);
       cp = eos+1;
     }
-  } else if (version != 0 && result->n_intro_points != 0) {
-    result->intro_point_extend_info =
-      tor_malloc_zero(sizeof(extend_info_t*)*result->n_intro_points);
-    result->intro_points =
-      tor_malloc_zero(sizeof(char*)*result->n_intro_points);
-    for (i=0;i<result->n_intro_points;++i) {
-      extend_info_t *info = result->intro_point_extend_info[i] =
-        tor_malloc_zero(sizeof(extend_info_t));
-      int klen;
-      if (end-cp < 8+DIGEST_LEN) goto truncated;
-      info->addr = ntohl(get_uint32(cp));
-      info->port = ntohs(get_uint16(cp+4));
-      memcpy(info->identity_digest, cp+6, DIGEST_LEN);
-      info->nickname[0] = '$';
-      base16_encode(info->nickname+1, sizeof(info->nickname)-1,
-                    info->identity_digest, DIGEST_LEN);
-      result->intro_points[i] = tor_strdup(info->nickname);
-      klen = ntohs(get_uint16(cp+6+DIGEST_LEN));
-      cp += 8+DIGEST_LEN;
-      if (end-cp < klen) goto truncated;
-      if (!(info->onion_key = crypto_pk_asn1_decode(cp,klen))) {
-        log_warn(LD_PROTOCOL,
-                 "Internal error decoding onion key for intro point.");
-        goto error;
-      }
-      cp += klen;
-    }
   }
   keylen = crypto_pk_keysize(result->pk);
   tor_assert(end-cp >= 0);
@@ -534,7 +455,7 @@
   tor_assert(pk);
   if (crypto_pk_get_digest(pk, buf) < 0)
     return -1;
-  base32_encode(out, REND_SERVICE_ID_LEN+1, buf, 10);
+  base32_encode(out, REND_SERVICE_ID_LEN + 1, buf, 10);
   return 0;
 }
 
@@ -551,12 +472,17 @@
  * rend_cache_entry_t. */
 static strmap_t *rend_cache = NULL;
 
+/** Map from descriptor id to rend_cache_entry_t; only for hidden service
+ * directories. */
+static digestmap_t *rend_cache_v2_dir = NULL;
+
 /** Initializes the service descriptor cache.
  */
 void
 rend_cache_init(void)
 {
   rend_cache = strmap_new();
+  rend_cache_v2_dir = digestmap_new();
 }
 
 /** Helper: free storage held by a single service descriptor cache entry. */
@@ -574,7 +500,9 @@
 rend_cache_free_all(void)
 {
   strmap_free(rend_cache, _rend_cache_entry_free);
+  digestmap_free(rend_cache_v2_dir, _rend_cache_entry_free);
   rend_cache = NULL;
+  rend_cache_v2_dir = NULL;
 }
 
 /** Removes all old entries from the service descriptor cache.
@@ -600,63 +528,110 @@
   }
 }
 
-/** Return the version number (1 or 2) of <b>query</b> iff is a syntactically
- * valid service ID (as generated by rend_get_service_id), or 0 otherwise. */
+/** Removes all old entries on v2 hidden service directories. */
+void
+rend_cache_clean_v2_dir(void)
+{
+  digestmap_iter_t *iter;
+  const char *key;
+  void *val;
+  rend_cache_entry_t *ent;
+  time_t cutoff;
+  cutoff = time(NULL) - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
+  for (iter = digestmap_iter_init(rend_cache_v2_dir); !digestmap_iter_done(iter); ) {
+    digestmap_iter_get(iter, &key, &val);
+    ent = (rend_cache_entry_t*)val;
+    if (ent->parsed->timestamp < cutoff) {
+      iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter);
+      _rend_cache_entry_free(ent);
+    } else {
+      iter = digestmap_iter_next(rend_cache_v2_dir, iter);
+    }
+  }
+}
+/** Determines whether <b>a</b> is in the interval of <b>b</b> (excluded) and
+ * <b>c</b> (included) in a circular digest ring; returns 1 if this is the
+ * case, and 0 otherwise.
+ */
 int
+hs_dir_is_in_interval(const char *a, const char *b, const char *c)
+{
+  tor_assert(a);
+  tor_assert(b);
+  tor_assert(c);
+  /* There are five cases in which a is outside the interval ]b,c]: */
+  if ((memcmp(a, b, DIGEST_LEN) == 0) || /* 1. a == b (b is excluded) */
+      /* 2. b == c (interval is empty) */
+      (memcmp(b, c, DIGEST_LEN) == 0) ||
+      /* 3. a b c */
+      (memcmp(a, b, DIGEST_LEN) <= 0 && memcmp(b, c, DIGEST_LEN) < 0) ||
+      /* 4. c a b */
+      (memcmp(c, a, DIGEST_LEN) < 0 && memcmp(a, b, DIGEST_LEN) <= 0) ||
+      /* 5. b c a */
+      (memcmp(b, c, DIGEST_LEN) < 0 && memcmp(c, a, DIGEST_LEN) < 0))
+    return 0;
+  /* In the other cases, a is inside the interval. */
+  else
+    return 1;
+} 
+
+/** cleans up all values for which i am not responsible */
+void
+rend_cache_clean_up(void)
+{
+  digestmap_iter_t *iter;
+  const char *key;
+  void *val;
+  rend_cache_entry_t *ent;
+  for (iter = digestmap_iter_init(rend_cache_v2_dir);
+       !digestmap_iter_done(iter); ) {
+  	char key_digest[DIGEST_LEN];
+    digestmap_iter_get(iter, &key, &val);
+    ent = (rend_cache_entry_t*)val;
+    base32_decode(key_digest, DIGEST_LEN, key, REND_DESC_ID_V2_LEN);
+    if (!responsible_for_desc_id(key_digest)) {
+      iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter);
+      _rend_cache_entry_free(ent);
+    } else {
+      iter = digestmap_iter_next(rend_cache_v2_dir, iter);
+    }
+  }
+}
+
+/** Return the version number (0 or 2) of <b>query</b> iff is a syntactically
+ * valid service ID (as generated by rend_get_service_id), or -1 otherwise. */
+int
 rend_valid_service_id(const char *query)
 {
   if (strlen(query) == REND_SERVICE_ID_LEN) {
     if (strspn(query, BASE32_CHARS) == REND_SERVICE_ID_LEN)
-      return 1;
+      return 0;
   } else if (strlen(query) == 16+1+24) {
     if (strspn(query, BASE32_CHARS) == 16 &&
         strspn(query+16, ".") == 1 &&
         strspn(query+17, BASE32_CHARS) == 24)
       return 2;
   }
-  return 0;
+  return -1;
 }
 
 /** If we have a cached rend_cache_entry_t for the service ID <b>query</b>,
- * set *<b>e</b> to that entry and return 1.  Else return 0.  If
- * <b>version</b> is nonnegative, only return an entry in that descriptor
- * format version. Otherwise (if <b>version</b> is negative), return the most
- * recent format we have.
+ * set *<b>e</b> to that entry and return 1.  Else return 0.  Only return an
+ * entry in that descriptor format version.
  */
 int
 rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e)
 {
-  char key[REND_SERVICE_ID_LEN+2]; /* 2<query>\0, 1<query>\0, or 0<query>\0 */
+  char key[REND_SERVICE_ID_LEN+2]; /* 2<query>\0   or   0<query>\0 */
+  tor_assert(version == 0 || version == 2);
   tor_assert(rend_cache);
-log_warn(LD_DIR, "KL6 rend_cache_lookup_entry, query is %s, version is %d", query, version);
-  if (rend_valid_service_id(query) != 1) {
-log_warn(LD_DIR, "KL6 rend_cache_lookup_entry, no valid service id %s", query);
+  if (rend_valid_service_id(query) != 0)
     return -1;
-  }
   *e = NULL;
-  /* version -1 or 2 */
-  if (version < 0 || version == 2) {
-log_warn(LD_DIR, "KL6 rend_cache_lookup_entry, looking up v2");
-    tor_snprintf(key, sizeof(key), "2%s", query);
-    *e = strmap_get_lc(rend_cache, key);
-  }
-  /* version -1 or 1 */
-  if (!*e && (version < 0 || version == 1)) {
-log_warn(LD_DIR, "KL6 rend_cache_lookup_entry, looking up v1");
-    tor_snprintf(key, sizeof(key), "1%s", query);
-    *e = strmap_get_lc(rend_cache, key);
-  }
-  /* version -1 or 0 */
-  if (!*e && version < 1) {
-log_warn(LD_DIR, "KL6 rend_cache_lookup_entry, looking up v0");
-    tor_snprintf(key, sizeof(key), "0%s", query);
-    *e = strmap_get_lc(rend_cache, key);
-  }
-  if (!*e) {
-log_warn(LD_DIR, "KL6 rend_cache_lookup_entry, nothing found");
+  tor_snprintf(key, sizeof(key), "%d%s", version, query);
+  *e = strmap_get_lc(rend_cache, key);
+  if (!*e)
     return 0;
-  }
-log_warn(LD_DIR, "KL6 rend_cache_lookup_entry, found something!");
   return 1;
 }
 
@@ -681,52 +656,134 @@
   return 1;
 }
 
-/** query is 32 chars long, write result in e.
+/** Lookup the v2 service descriptor with binary-encoded <b>desc_id</b> and
+ * copy the pointer to it to <b>desc</b>.
  */
 int
-rend_cache_lookup_v2_dir(const char *query, const char **desc,
-                         size_t *desc_len)
+rend_cache_lookup_v2_dir(const char *desc_id, char **desc)
 {
-  rend_cache_entry_t *e = NULL;
-  tor_assert(rend_cache);
-  tor_assert(strlen(query) == 32);
-  char key[REND_DESC_ID_V2_LEN+2]; /* 2<query>\0 */
-  tor_snprintf(key, sizeof(key), "2%s", query);
-  e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key);
+  rend_cache_entry_t *e;
+  tor_assert(rend_cache_v2_dir);
+  tor_assert(desc_id);
+  /* Determine if we are responsible. */
+  if (responsible_for_desc_id(desc_id) < 0) {
+    log_info(LD_REND, "Could not answer fetch request for v2 descriptor; "
+                      "either we are no hidden service directory, or we are "
+                      "not responsible for the requested ID.");
+    return -1;
+  }
+  /* Lookup descriptor and return. */
+  e = (rend_cache_entry_t*) digestmap_get(rend_cache_v2_dir, desc_id);
   if (e) {
     *desc = e->desc;
-    *desc_len = e->len;
     return 1;
   }
   return 0;
 }
 
-/** here happens the actual storing of a previously parsed descriptor, whether
- * client-side or directory-side.
- *
+/** Lookup all v2 service descriptors in the interval that is encoded in
+ * <b>query</b> as two base32-encoded descriptor IDs separated by a minus,
+ * ecluding the start ID and including the end ID, and write them to (the
+ * newly allocated) string <b>descs</b>.
+ */
+int
+rend_cache_lookup_v2_replicas(const char *query, char **descs)
+{
+  smartlist_t *query_tmp;
+  const char *from_id_base32;
+  const char *to_id_base32;
+  char from_id[20];
+  char to_id[20];
+  smartlist_t *results;
+  digestmap_iter_t *iter;
+  rend_cache_entry_t *ent;
+  const char *key;
+  void *val;
+  size_t overall_len = 10;
+  size_t written = 0;
+  tor_assert(query);
+  /* Are we acting as hidden service directory? */
+  if (!acting_as_hs_dir()) {
+  	log_info(LD_REND, "Could not answer v2 replica request, because we are "
+  	                  "not acting as hs dir.");
+    return -1;
+  }
+  /* Parse the two IDs from query. */
+  query_tmp = smartlist_create();
+  tor_assert(query);
+  smartlist_split_string(query_tmp, query, "-", 0, 2);
+  if (query_tmp->num_used != 2) {
+    log_info(LD_REND, "Lookup request for replicas is not well-formatted!");
+    return -1;
+  }
+  from_id_base32 = query_tmp->list[0];
+  to_id_base32 = query_tmp->list[1];
+  base32_decode(from_id, 20, from_id_base32, 32);
+  base32_decode(to_id, 20, to_id_base32, 32);
+  smartlist_free(query_tmp);
+  /* Iterate over all stored descriptors and add those descriptors matching
+   * the query to a result list. */
+  results = smartlist_create();
+  for (iter = digestmap_iter_init(rend_cache_v2_dir);
+       !digestmap_iter_done(iter);
+       iter = digestmap_iter_next(rend_cache_v2_dir, iter)) {
+    digestmap_iter_get(iter, &key, &val);
+    ent = (rend_cache_entry_t *)val;
+    /* Is desc ID in the range that we are (directly or indirectly) responsible
+     * for? */
+    if (!responsible_for_desc_id(key))
+      continue;
+    /* Was this descriptor requested? */
+    if (!hs_dir_is_in_interval(key, from_id, to_id))
+      continue;
+    /* Add descriptor to result list. */
+    smartlist_add(results, ent->desc);
+    overall_len += strlen(ent->desc) + 2;
+  }
+  /* Assemble a single string with all results. */
+  *descs = tor_malloc_zero(overall_len);
+  SMARTLIST_FOREACH(results, const char *, desc, {
+    strlcpy(*descs + written, desc, strlen(desc));
+    written += strlen(*descs + written);
+    *(*descs + written++) = '\n';
+  });
+  smartlist_free(results);
+  
+  /* TODO114 this _might_ fail... */ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+  
+  return 0;
+}
+
+/** Parse *desc, calculate its service id, and store it in the cache.
  * If we have a newer descriptor with the same ID, ignore this one.
  * If we have an older descriptor with the same ID, replace it.
  * Return -1 if it's malformed or otherwise rejected; return 0 if
  * it's the same or older than one we've already got; return 1 if
  * it's novel. The published flag tells us if we store the descriptor
  * in our role as directory (1) or if we cache it as client (0).
- * The version can be either >= 2 for v2 descriptors or <= 1 for v0/v1
- * descriptors. If published is 0, secret_cookie needs to contain a key,
- * otherwise it is NULL.
- *
- * parsed = parsed descriptor, key = key under which it will be stored,
- * published = flag, whether this request was triggered by a directory store
- * operation (true) or by a client (false).
  */
-static int
-rend_cache_store_parsed(const char *query, rend_service_descriptor_t *parsed,
-                        const char *desc, size_t desc_len, const char *key,
-                        int published)
+int
+rend_cache_store(const char *desc, size_t desc_len, int published)
 {
   rend_cache_entry_t *e;
-  time_t now = time(NULL);
+  rend_service_descriptor_t *parsed;
+  char query[REND_SERVICE_ID_LEN+1];
+  char key[REND_SERVICE_ID_LEN+2]; /* 0<query>\0 */
+  time_t now;
   or_options_t *options = get_options();
-log_warn(LD_DIR, "KL6 storing desc under key %s", key);
+  tor_assert(rend_cache);
+  parsed = rend_parse_service_descriptor(desc,desc_len);
+  if (!parsed) {
+    log_warn(LD_PROTOCOL,"Couldn't parse service descriptor.");
+    return -1;
+  }
+  if (rend_get_service_id(parsed->pk, query)<0) {
+    log_warn(LD_BUG,"Couldn't compute service ID.");
+    rend_service_descriptor_free(parsed);
+    return -1;
+  }
+  tor_snprintf(key, sizeof(key), "0%s", query);
+  now = time(NULL);
   if (parsed->timestamp < now-REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) {
     log_fn(LOG_PROTOCOL_WARN, LD_REND,
            "Service descriptor %s is too old.", safe_str(query));
@@ -779,79 +836,197 @@
   return 1;
 }
 
-/** Parse *desc, calculate its service id, and request to store it in the
- * cache.
+/** Parse the v2 service descriptor(s) in <b>desc</b> and store it/them to the
+ * local rend cache. Don't attempt to decrypt the included list of introduction
+ * points (as we don't have the secret_cookie for it). 
+ *
+ * If we have a newer descriptor with the same ID, ignore this one.
+ * If we have an older descriptor with the same ID, replace it.
+ * Return -1 if it's malformed or otherwise rejected; return 0 if
+ * it's the same or older than one we've already got; return 1 if
+ * it's novel.
  */
 int
-rend_cache_store(const char *desc, size_t desc_len, int published)
+rend_cache_store_v2_dir(const char *desc)
 {
   rend_service_descriptor_t *parsed;
-  char query[REND_SERVICE_ID_LEN+1];
-  char key[REND_SERVICE_ID_LEN+2]; /* 1<query>\0  or  0<query>\0 */
-  tor_assert(rend_cache);
-  parsed = rend_parse_service_descriptor(desc,desc_len);
-  if (!parsed) {
-    log_warn(LD_PROTOCOL,"Couldn't parse service descriptor.");
+  char desc_id[DIGEST_LEN];
+  char *intro_content;
+  size_t intro_size;
+  char desc_id_base32[REND_DESC_ID_V2_LEN+1];
+  int number_parsed = 0;
+  const char **current_desc = (const char **)(&desc);
+  rend_cache_entry_t *e;
+  time_t now = time(NULL);
+  tor_assert(rend_cache_v2_dir);
+  tor_assert(desc);
+  if (!acting_as_hs_dir()) {
+  	/* Cannot store descs, because we are (currently) not acting as hs dir. */
     return -1;
   }
-  if (rend_get_service_id(parsed->pk, query)<0) {
-    log_warn(LD_BUG,"Couldn't compute service ID.");
-    rend_service_descriptor_free(parsed);
-    return -1;
+  while (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
+                                          &intro_size, current_desc, 1) >= 0) {
+    tor_assert(parsed);
+    /* We don't care about the introduction points. */
+    tor_free(intro_content);
+    /* For pretty log statements. */
+    base32_encode(desc_id_base32, REND_DESC_ID_V2_LEN + 1, desc_id, DIGEST_LEN);
+    /* Is desc ID in the range that we are (directly or indirectly) responsible
+     * for? */
+    if (responsible_for_desc_id(desc_id) < 0) {
+      log_info(LD_REND, "Service descriptor with desc ID %s is not in "
+                        "interval that we are responsible for.",
+               desc_id_base32);
+      rend_service_descriptor_free(parsed);
+      continue;
+    }
+    /* Is descriptor too old? */ 
+    if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) {
+      log_info(LD_REND, "Service descriptor with desc ID %s is too old.",
+               desc_id_base32);
+      rend_service_descriptor_free(parsed);
+      continue;
+    }
+    /* Is descriptor too far in the future? */
+    if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) {
+      log_info(LD_REND, "Service descriptor with desc ID %s is too far in the "
+                        "future.",
+               desc_id_base32);
+      rend_service_descriptor_free(parsed);
+      continue;
+    }
+    /* Do we already have a newer descriptor? */
+    e = (rend_cache_entry_t *)digestmap_get(rend_cache_v2_dir, desc_id);
+    if (e && e->parsed->timestamp > parsed->timestamp) {
+      log_info(LD_REND, "We already have a newer service descriptor with the "
+                        "same desc ID %s and version.", desc_id_base32);
+      rend_service_descriptor_free(parsed);
+      continue;
+    }
+    /* Do we already have this descriptor? */
+    if (e && !strcmp(desc, e->desc)) {
+      log_info(LD_REND, "We already have this service descriptor with desc "
+                        "ID %s.", desc_id_base32);
+      e->received = time(NULL);
+      rend_service_descriptor_free(parsed);
+      continue;
+    }
+    /* Store received descriptor. */
+    if (!e) {
+      e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+      digestmap_set(rend_cache_v2_dir, desc_id, e);
+    } else {
+      rend_service_descriptor_free(e->parsed);
+      tor_free(e->desc);
+    }
+    e->received = time(NULL);
+    e->parsed = parsed;
+    e->desc = tor_malloc(strlen(desc) + 1);
+    strncpy(e->desc, desc, strlen(desc));
+    log_debug(LD_REND, "Successfully stored service descriptor with desc ID "
+                       "'%s' and len %d.", desc_id_base32, strlen(desc));
+    number_parsed++;
   }
-  tor_snprintf(key, sizeof(key), "%c%s", parsed->version?'1':'0', query);
-  return rend_cache_store_parsed(query, parsed, desc, desc_len, key,
-                                 published);
+  log_info(LD_REND, "Parsed and added %d descriptor%s.",
+           number_parsed, number_parsed > 1 ? "s" : "");
+  return number_parsed;
 }
 
-/** store a v2 desc in the local cache under its version and service id and
- * decrypt the included list of introduction points with secret_cookie
+/** Parse the v2 service descriptor in <b>desc</b>, decrypt the included list
+ * of introduction points with the base32-encoded <b>secret_cookie</b>, and
+ * store the descriptor to the local cache under its version and service id. 
+ *
+ * If we have a newer descriptor with the same ID, ignore this one.
+ * If we have an older descriptor with the same ID, replace it.
+ * Return -1 if it's malformed or otherwise rejected; return 0 if
+ * it's the same or older than one we've already got; return 1 if
+ * it's novel.
  */
 int
 rend_cache_store_v2_client(const char *desc, const char *secret_cookie)
 {
-  rend_service_descriptor_t *parsed;
+  rend_service_descriptor_t *parsed = NULL;
+  char desc_id[DIGEST_LEN];
+  char *intro_content = NULL;
+  size_t intro_size;
+  const char **first_desc = (const char **)(&desc);
+  time_t now = time(NULL);
   char key[REND_SERVICE_ID_LEN+2];
-  char query[REND_SERVICE_ID_LEN+1];
+  char service_id[REND_SERVICE_ID_LEN+1];
+  rend_cache_entry_t *e;
   tor_assert(rend_cache);
+  tor_assert(desc);
   tor_assert(secret_cookie);
   tor_assert(strlen(secret_cookie) == 24);
-  parsed = tor_malloc_zero(sizeof(rend_service_descriptor_t));
-  if (rend_parse_v2_service_descriptor(parsed, desc) < 0) {
-    log_warn(LD_FS, "Could not parse descriptor");
+  /* Parse the descriptor. */
+  if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
+                                       &intro_size, first_desc, 0) < 0) {
+    log_warn(LD_REND, "Could not parse descriptor.");
     return -1;
   }
-  if (rend_get_service_id(parsed->pk, query)<0) {
-    log_warn(LD_BUG,"Couldn't compute service ID.");
+  /* Compute service ID from public key. */
+  if (rend_get_service_id(parsed->pk, service_id)<0) {
+    log_warn(LD_REND, "Couldn't compute service ID.");
     rend_service_descriptor_free(parsed);
+    tor_free(intro_content);
     return -1;
   }
-  if (rend_decrypt_introduction_points(parsed, secret_cookie) < 0) {
+  /* Decrypt introduction points with secret cookie. */
+  if (rend_decrypt_introduction_points(parsed, secret_cookie, intro_content,
+      intro_size) < 0) {
     log_warn(LD_PROTOCOL,"Couldn't decrypt introduction points.");
+    rend_service_descriptor_free(parsed);
+    tor_free(intro_content);
     return -1;
   }
-  tor_snprintf(key, sizeof(key), "2%s", query);
-  return rend_cache_store_parsed(query, parsed, desc, strlen(desc), key, 0);
-}
-
-/** store a v2 desc in the local cache under its version and descriptor ID,
- * but don't attempt to decrypt the included list of introduction points
- * (as we don't have the secret_cookie for it).
- */
-int
-rend_cache_store_v2_dir(const char *desc)
-{
-  rend_service_descriptor_t *parsed;
-  char key[REND_DESC_ID_V2_LEN+2];
-  tor_assert(rend_cache);
-  parsed = tor_malloc_zero(sizeof(rend_service_descriptor_t));
-  if (rend_parse_v2_service_descriptor(parsed, desc) < 0) {
-    log_warn(LD_FS, "Could not parse descriptor");
+  /* We don't need the encrypted introduction points any longer. */
+  tor_free(intro_content);
+  /* Is descriptor too old? */
+  if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) {
+    log_warn(LD_REND, "Service descriptor with service ID %s is too old.",
+             service_id);
+    rend_service_descriptor_free(parsed);
     return -1;
   }
-  tor_snprintf(key, sizeof(key), "2%s", parsed->desc_id[0]);
-  return rend_cache_store_parsed(parsed->desc_id[0], parsed, desc,
-                                 strlen(desc), key, 1);
+  /* Is descriptor too far in the future? */
+  if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) {
+    log_warn(LD_REND, "Service descriptor with service ID %s is too far in "
+                      "the future.", service_id);
+    rend_service_descriptor_free(parsed);
+    return -1;
+  }
+  /* Do we already have a newer descriptor? */
+  tor_snprintf(key, sizeof(key), "2%s", service_id);
+  e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key);
+  if (e && e->parsed->timestamp > parsed->timestamp) {
+    log_info(LD_REND, "We already have a newer service descriptor for "
+                      "service ID %s with the same desc ID and version.",
+             service_id);
+    rend_service_descriptor_free(parsed);
+    return 0;
+  }
+  /* Do we already have this descriptor? */
+  if (e && !strcmp(desc, e->desc)) {
+    log_info(LD_REND,"We already have this service descriptor %s.",
+             service_id);
+    e->received = time(NULL);
+    rend_service_descriptor_free(parsed);
+    return 0;
+  }
+  if (!e) {
+    e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+    strmap_set_lc(rend_cache, key, e);
+  } else {
+    rend_service_descriptor_free(e->parsed);
+    tor_free(e->desc);
+  }
+  e->received = time(NULL);
+  e->parsed = parsed;
+  e->desc = tor_malloc_zero(strlen(desc) + 1);
+  strncpy(e->desc, desc, strlen(desc));
+  log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.",
+            service_id, strlen(desc));
+  return 1;
 }
 
 /** Called when we get a rendezvous-related relay cell on circuit
@@ -910,3 +1085,11 @@
   return strmap_size(rend_cache);
 }
 
+/** Return the number of entries in our directoy-side v2 rendezvous descriptor
+ * cache. */
+int
+rend_cache_size_v2_dir(void)
+{
+  return digestmap_size(rend_cache_v2_dir);
+}
+

Modified: tor/branches/114-dist-storage/src/or/rendmid.c
===================================================================
--- tor/branches/114-dist-storage/src/or/rendmid.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/rendmid.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -132,6 +132,9 @@
   char serviceid[REND_SERVICE_ID_LEN+1];
   char nak_body[1];
 
+  log_info(LD_REND, "Received an INTRODUCE1 request on circuit %d",
+           circ->p_circ_id);
+
   if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_conn) {
     log_warn(LD_PROTOCOL,
              "Rejecting INTRODUCE1 on non-OR or non-edge circuit %d.",
@@ -177,7 +180,7 @@
              "Unable to send INTRODUCE2 cell to Tor client.");
     goto err;
   }
-  /* And sent an ack down Alice's circuit.  Empty body means succeeded. */
+  /* And send an ack down Alice's circuit.  Empty body means succeeded. */
   if (relay_send_command_from_edge(0,TO_CIRCUIT(circ),
                                    RELAY_COMMAND_INTRODUCE_ACK,
                                    NULL,0,NULL)) {
@@ -210,6 +213,9 @@
   char hexid[9];
   int reason = END_CIRC_REASON_TORPROTOCOL;
 
+  log_info(LD_REND, "Received an ESTABLISH_RENDEZVOUS request on circuit %d",
+           circ->p_circ_id);
+
   if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_conn) {
     log_warn(LD_PROTOCOL,
              "Tried to establish rendezvous on non-OR or non-edge circuit.");

Modified: tor/branches/114-dist-storage/src/or/rendservice.c
===================================================================
--- tor/branches/114-dist-storage/src/or/rendservice.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/rendservice.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -326,10 +326,10 @@
 rend_get_hostname2(rend_service_t *service, char *hostname2)
 {
   char permanent_id_base32[16+1];
-  base32_encode(permanent_id_base32, 16+1, service->pk_digest, 10);
+  base32_encode(permanent_id_base32, 16 + 1, service->pk_digest, 10);
   if (tor_snprintf(hostname2, 50, "%s.%s.onion\n", permanent_id_base32,
                    service->secret_cookie) < 0) {
-    log_warn(LD_BUG, "could not encode hostname");
+    log_warn(LD_BUG, "Could not encode hostname.");
     return -1;
   }
   return 0;
@@ -370,6 +370,7 @@
     s->private_key = init_key_from_file(fname, 1, LOG_ERR);
     if (!s->private_key)
       return -1;
+
     /* Try to read secret_cookie file; if that fails, create new file */
     if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
         strlcat(fname,PATH_SEPARATOR"secret_cookie",sizeof(fname))
@@ -382,13 +383,14 @@
       char secret_cookie_bin[15];
       crypto_rand(secret_cookie_bin, 15);
       s->secret_cookie = tor_malloc_zero(24+1);
-      base32_encode(s->secret_cookie, 24+1, secret_cookie_bin, 15);
+      base32_encode(s->secret_cookie, 24 + 1, secret_cookie_bin, 15);
       if (write_str_to_file(fname, s->secret_cookie, 0) < 0) {
         log_warn(LD_CONFIG, "Could not write secret_cookie file:"
                " \"%s\".", s->directory);
         return -1;
       }
     }
+
     /* Create service file */
     if (rend_get_service_id(s->private_key, s->service_id)<0) {
       log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
@@ -420,6 +422,7 @@
     }
     if (write_str_to_file(fname2, hostname2, 0) < 0)
       return -1;
+    log_info(LD_REND, "Hidden service (v0 and v2) configured.");
   }
   return 0;
 }
@@ -479,7 +482,7 @@
   int circ_needs_uptime;
   int reason = END_CIRC_REASON_TORPROTOCOL;
 
-  base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
+  base32_encode(serviceid, REND_SERVICE_ID_LEN + 1,
                 circuit->rend_pk_digest,10);
   log_info(LD_REND, "Received INTRODUCE2 cell for service %s on circ %d.",
            escaped(serviceid), circuit->_base.n_circ_id);
@@ -507,7 +510,7 @@
     return -1;
   }
   if (memcmp(circuit->rend_pk_digest, request, DIGEST_LEN)) {
-    base32_encode(serviceid, REND_SERVICE_ID_LEN+1, request, 10);
+    base32_encode(serviceid, REND_SERVICE_ID_LEN + 1, request, 10);
     log_warn(LD_REND, "Got an INTRODUCE2 cell for the wrong service (%s).",
              escaped(serviceid));
     return -1;
@@ -781,7 +784,7 @@
   tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
   tor_assert(circuit->cpath);
 
-  base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
+  base32_encode(serviceid, REND_SERVICE_ID_LEN + 1,
                 circuit->rend_pk_digest,10);
 
   service = rend_service_get_by_pk_digest(circuit->rend_pk_digest);
@@ -838,6 +841,7 @@
                                size_t request_len)
 {
   rend_service_t *service;
+  char serviceid[REND_SERVICE_ID_LEN+1];
   (void) request;
   (void) request_len;
 
@@ -855,6 +859,12 @@
   service->desc_is_dirty = time(NULL);
   circuit->_base.purpose = CIRCUIT_PURPOSE_S_INTRO;
 
+  base32_encode(serviceid, REND_SERVICE_ID_LEN + 1,
+                circuit->rend_pk_digest, 10);
+  log_info(LD_REND,
+           "Received INTRO_ESTABLISHED cell on circuit %d for service %s",
+           circuit->_base.n_circ_id, serviceid);
+
   return 0;
  err:
   circuit_mark_for_close(TO_CIRCUIT(circuit), END_CIRC_REASON_TORPROTOCOL);
@@ -881,7 +891,7 @@
   tor_assert(hop);
 
   base16_encode(hexcookie,9,circuit->rend_cookie,4);
-  base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
+  base32_encode(serviceid, REND_SERVICE_ID_LEN + 1,
                 circuit->rend_pk_digest,10);
 
   log_info(LD_REND,
@@ -980,56 +990,92 @@
 static void
 upload_service_descriptor(rend_service_t *service)
 {
+  time_t now = time(NULL);
+  int rendpostperiod;
+  char serviceid[REND_SERVICE_ID_LEN+1];
+
   /* Update the descriptor. */
   rend_service_update_descriptor(service);
 
-  /* Upload v0 or v1 descriptor? */
+  rendpostperiod = get_options()->RendPostPeriod;
+
+  /* Upload v0 descriptor? */
   if (get_options()->PublishHidServDescriptors) {
     char *desc;
     size_t desc_len;
-
     /* Encode the descriptor. */
     if (rend_encode_service_descriptor(service->desc,
-                                       0, // TODO114 is 0 correct?
                                        service->private_key,
                                        &desc, &desc_len)<0) {
       log_warn(LD_BUG, "Internal error: couldn't encode service descriptor; "
                "not uploading.");
       return;
     }
-    
-log_warn(LD_REND, "KL6 this is our v1 descriptor of len %d: %s", desc_len, hex_str(desc, desc_len));
 
     /* Post it to the dirservers */
+    rend_get_service_id(service->desc->pk, serviceid);
+    log_info(LD_REND, "Sending publish request for hidden service %s",
+             serviceid);
     directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_RENDDESC,
-                                 HIDSERV_AUTHORITY, desc, desc_len, 0);
+                               ROUTER_PURPOSE_GENERAL,
+                               HIDSERV_AUTHORITY, desc, desc_len, 0);
     tor_free(desc);
+    service->next_upload_time = now + rendpostperiod;
   }
 
   /* Upload v2 descriptor? */
-  if (get_options()->PublishV2HidServDescriptors) {
-    time_t now = time(NULL);
-    char current_desc[4000];
-    char next_desc[4000];
-
-    /* Encode the current and the next descriptor. */
-    if (rend_encode_v2_descriptor(current_desc, next_desc, service->desc, now,
-                                           service->secret_cookie) < 0) {
+  if (get_options()->PublishV2HidServDescriptors && have_enough_hs_dirs()) {
+  	int seconds_valid;
+    smartlist_t *desc_strs = smartlist_create();
+    char desc_ids[NUMBER_OF_NON_CONSECUTIVE_REPLICAS][DIGEST_LEN];
+    int i;
+    /* Encode the current descriptor. */
+log_info(LD_REND, "Upload v2 descriptor 1.");
+    seconds_valid = rend_encode_v2_descriptors(desc_strs, desc_ids,
+                                               service->desc, now,
+                                               service->secret_cookie, 0);
+log_info(LD_REND, "Upload v2 descriptor 2.");
+    if (seconds_valid < 0) {
       log_warn(LD_BUG, "Internal error: couldn't encode service descriptor; "
                "not uploading.");
       return;
     }
-
-    /* Post the current descriptor to the hidden service directories. */
-    directory_post_to_hs_dir(service->desc->desc_id[0],
-        current_desc);
-
-    /* Post also the next descriptor, if necessary. TODO make 6 configurable */
-    if (service->desc->seconds_valid < 6)
-      directory_post_to_hs_dir(service->desc->desc_id[1],
-        next_desc);
+log_info(LD_REND, "Upload v2 descriptor 3.");
+    /* Post the current descriptors to the hidden service directories. */
+    directory_post_to_hs_dir(serviceid, desc_ids, desc_strs, seconds_valid);
+log_info(LD_REND, "Upload v2 descriptor 4.");
+    /* Free memory for descriptors. */
+    for (i = 0; i < NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++)
+      tor_free(desc_strs->list[i]);
+log_info(LD_REND, "Upload v2 descriptor 5.");
+    smartlist_free(desc_strs);
+log_info(LD_REND, "Upload v2 descriptor 6.");
+    /* Post also the next descriptors, if necessary. */
+    if (seconds_valid < TIME_PERIOD_TWO) {
+log_info(LD_REND, "Upload v2 descriptor 7.");
+      desc_strs = smartlist_create();
+log_info(LD_REND, "Upload v2 descriptor 8.");
+      seconds_valid = rend_encode_v2_descriptors(desc_strs, desc_ids,
+                                                 service->desc, now,
+                                                 service->secret_cookie, 1);
+log_info(LD_REND, "Upload v2 descriptor 9.");
+      directory_post_to_hs_dir(serviceid, desc_ids, desc_strs, seconds_valid);
+log_info(LD_REND, "Upload v2 descriptor 10.");
+      /* Free memory for descriptors. */
+      for (i = 0; i < NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++)
+        tor_free(desc_strs->list[i]);
+log_info(LD_REND, "Upload v2 descriptor 11.");
+      smartlist_free(desc_strs);
+log_info(LD_REND, "Upload v2 descriptor 12.");
+    }
+log_info(LD_REND, "Upload v2 descriptor 13.");
+    if (seconds_valid - TIME_PERIOD_TWO < rendpostperiod)
+      service->next_upload_time = now + seconds_valid - TIME_PERIOD_TWO + 1;
+    else
+      service->next_upload_time = now + rendpostperiod;
   }
 
+  /* Unmark dirty flag of this service. */ 
   service->desc_is_dirty = 0;
 }
 
@@ -1172,14 +1218,14 @@
       service->next_upload_time =
         now + crypto_rand_int(2*rendpostperiod);
     }
-    if (service->next_upload_time < now ||
+    if ((service->next_upload_time &&
+         service->next_upload_time < now) ||
         (service->desc_is_dirty &&
          service->desc_is_dirty < now-30)) {
       /* if it's time, or if the directory servers have a wrong service
        * descriptor and ours has been stable for 30 seconds, upload a
        * new one of each format. */
       upload_service_descriptor(service);
-      service->next_upload_time = now + rendpostperiod;
     }
   }
 }
@@ -1243,7 +1289,7 @@
 
   tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
   log_debug(LD_REND,"beginning to hunt for addr/port");
-  base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
+  base32_encode(serviceid, REND_SERVICE_ID_LEN + 1,
                 circ->rend_pk_digest,10);
   service = rend_service_get_by_pk_digest(circ->rend_pk_digest);
   if (!service) {

Modified: tor/branches/114-dist-storage/src/or/router.c
===================================================================
--- tor/branches/114-dist-storage/src/or/router.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/router.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -615,7 +615,7 @@
                                0, me->cache_info.identity_digest,
                                DIR_PURPOSE_FETCH_SERVERDESC,
                                ROUTER_PURPOSE_GENERAL,
-                               1, "authority", NULL, 0);
+                               1, "authority.z", NULL, 0);
 
     control_event_server_status(LOG_NOTICE,
                                 "CHECKING_REACHABILITY DIRADDRESS=%s:%d",
@@ -714,6 +714,14 @@
 {
   return authdir_mode(options) && options->V2AuthoritativeDir != 0;
 }
+/** Return true iff we believe ourselves to be a v3 authoritative
+ * directory server.
+ */
+int
+authdir_mode_v3(or_options_t *options)
+{
+  return authdir_mode(options) && options->V3AuthoritativeDir != 0;
+}
 /** Return true iff we are an authoritative directory server that
  * is willing to receive or serve descriptors on its dirport.
  */
@@ -909,8 +917,11 @@
   }
   msg[desc_len+extra_len] = 0;
 
-  directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_DIR, auth,
-                               msg, desc_len, extra_len);
+  directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_DIR,
+                               (auth & BRIDGE_AUTHORITY) ?
+                                 ROUTER_PURPOSE_BRIDGE :
+                                 ROUTER_PURPOSE_GENERAL,
+                               auth, msg, desc_len, extra_len);
   tor_free(msg);
 }
 
@@ -1388,7 +1399,9 @@
   size_t written;
   int result=0;
   addr_policy_t *tmpe;
+#ifdef INCLUDE_BW_INFO_IN_ROUTERDESCS
   char *bandwidth_usage;
+#endif
   char *family_line;
   or_options_t *options = get_options();
 
@@ -1423,8 +1436,10 @@
   /* Encode the publication time. */
   format_iso_time(published, router->cache_info.published_on);
 
+#ifdef INCLUDE_BW_INFO_IN_ROUTERDESCS
   /* How busy have we been? */
   bandwidth_usage = rep_hist_get_bandwidth_lines(0);
+#endif
 
   if (router->declared_family && smartlist_len(router->declared_family)) {
     size_t n;
@@ -1466,14 +1481,21 @@
     extra_info_digest,
     options->DownloadExtraInfo ? "opt caches-extra-info\n" : "",
     onion_pkey, identity_pkey,
-    family_line, bandwidth_usage,
+    family_line,
+#ifdef INCLUDE_BW_INFO_IN_ROUTERDESCS
+    bandwidth_usage,
+#else
+    "",
+#endif
     we_are_hibernating() ? "opt hibernating 1\n" : "",
     options->HSDir ? "opt hidden-service-dir\n" : "");
 
   tor_free(family_line);
   tor_free(onion_pkey);
   tor_free(identity_pkey);
+#ifdef INCLUDE_BW_INFO_IN_ROUTERDESCS
   tor_free(bandwidth_usage);
+#endif
 
   if (result < 0) {
     log_warn(LD_BUG,"descriptor snprintf #1 ran out of room!");

Modified: tor/branches/114-dist-storage/src/or/routerlist.c
===================================================================
--- tor/branches/114-dist-storage/src/or/routerlist.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/routerlist.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -65,6 +65,9 @@
  * about.  This list is kept sorted by published_on. */
 static smartlist_t *networkstatus_list = NULL;
 
+/** Most recently received v3 consensus network status. */
+static networkstatus_vote_t *current_consensus = NULL;
+
 /** Global list of local_routerstatus_t for each router, known or unknown.
  * Kept sorted by digest. */
 static smartlist_t *routerstatus_list = NULL;
@@ -183,9 +186,9 @@
 
   tor_snprintf(filename,sizeof(filename),"%s"PATH_SEPARATOR"cached-certs",
                get_options()->DataDirectory);
-  contents = read_file_to_str(filename, 0, NULL);
+  contents = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
   if (!contents)
-    return -1;
+    return 0;
   r = trusted_dirs_load_certs_from_string(contents, 1);
   tor_free(contents);
   return r;
@@ -204,6 +207,7 @@
 
   for (s = contents; *s; s = eos) {
     authority_cert_t *cert = authority_cert_parse_from_string(s, &eos);
+    int found = 0;
     if (!cert)
       break;
     ds = trusteddirserver_get_by_v3_auth_digest(
@@ -214,25 +218,34 @@
       authority_cert_free(cert);
       continue;
     }
+    if (!ds->v3_certs)
+      ds->v3_certs = smartlist_create();
 
-    if (ds->v3_cert) {
-      if (ds->v3_cert->expires < cert->expires) {
-        authority_cert_free(ds->v3_cert);
-        ds->v3_cert = NULL; /* redundant, but let's be safe. */
-      } else {
-        /* This also covers the case where the certificate is the same
-         * as the one we have. */
-        authority_cert_free(cert);
-        continue;
-      }
-    }
+    SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, c,
+      {
+        if (memcmp(c->cache_info.signed_descriptor_digest,
+                   cert->cache_info.signed_descriptor_digest,
+                   DIGEST_LEN)) {
+          /* we already have this one. continue. */
+          authority_cert_free(cert);
+          found = 1;
+          break;
+        }
+      });
 
+    if (found)
+      continue;
+
     cert->cache_info.signed_descriptor_body = tor_strndup(s, eos-s);
     cert->cache_info.signed_descriptor_len = eos-s;
-    ds->v3_cert = cert;
+    smartlist_add(ds->v3_certs, cert);
+
     if (!from_store)
       trusted_dir_servers_certs_changed = 1;
   }
+
+  trusted_dirs_flush_certs_to_disk();
+
   return 0;
 }
 
@@ -241,17 +254,25 @@
 trusted_dirs_flush_certs_to_disk(void)
 {
   char filename[512];
-  smartlist_t *chunks = smartlist_create();
+  smartlist_t *chunks;
 
+  if (!trusted_dir_servers_certs_changed)
+    return;
+
+  chunks = smartlist_create();
+
   tor_snprintf(filename,sizeof(filename),"%s"PATH_SEPARATOR"cached-certs",
                get_options()->DataDirectory);
   SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds,
   {
-      if (ds->v3_cert) {
-        sized_chunk_t *c = tor_malloc(sizeof(sized_chunk_t));
-        c->bytes = ds->v3_cert->cache_info.signed_descriptor_body;
-        c->len = ds->v3_cert->cache_info.signed_descriptor_len;
-        smartlist_add(chunks, c);
+      if (ds->v3_certs) {
+        SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, cert,
+          {
+            sized_chunk_t *c = tor_malloc(sizeof(sized_chunk_t));
+            c->bytes = cert->cache_info.signed_descriptor_body;
+            c->len = cert->cache_info.signed_descriptor_len;
+            smartlist_add(chunks, c);
+          });
       }
   });
   if (write_chunks_to_file(filename, chunks, 0)) {
@@ -263,6 +284,54 @@
   trusted_dir_servers_certs_changed = 0;
 }
 
+/** Remove all v3 authority certificates that have been superseded for more
+ * than 48 hours.  (If the most recent cert was published more than 48 hours
+ * ago, then we aren't going to get any consensuses signed with older
+ * keys.) */
+static void
+trusted_dirs_remove_old_certs(void)
+{
+#define OLD_CERT_LIFETIME (48*60*60)
+  SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds,
+    {
+      authority_cert_t *newest = NULL;
+      if (!ds->v3_certs)
+        continue;
+      SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, cert,
+          if (!newest || (cert->cache_info.published_on >
+                          newest->cache_info.published_on))
+            newest = cert);
+      SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, cert,
+          if (newest && (newest->cache_info.published_on >
+                         cert->cache_info.published_on + OLD_CERT_LIFETIME)) {
+            SMARTLIST_DEL_CURRENT(ds->v3_certs, cert);
+            authority_cert_free(cert);
+            trusted_dir_servers_certs_changed = 1;
+          });
+    });
+#undef OLD_CERT_LIFETIME
+
+  trusted_dirs_flush_certs_to_disk();
+}
+
+/** Return the v3 authority certificate with signing key matching
+ * <b>sk_digest</b>, for the authority with identity digest <b>id_digest</b>.
+ * Return NULL if no such authority is known. */
+authority_cert_t *
+authority_cert_get_by_digests(const char *id_digest,
+                              const char *sk_digest)
+{
+  trusted_dir_server_t *ds = trusteddirserver_get_by_v3_auth_digest(id_digest);
+
+  if (!ds || !ds->v3_certs)
+    return NULL;
+  SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, cert,
+    if (!memcmp(cert->signing_key_digest, sk_digest, DIGEST_LEN))
+      return cert; );
+
+  return NULL;
+}
+
 /* Router descriptor storage.
  *
  * Routerdescs are stored in a big file, named "cached-routers".  As new
@@ -501,6 +570,7 @@
   if (signed_descriptors)
     smartlist_free(signed_descriptors);
   tor_free(fname);
+  tor_free(fname_tmp);
   SMARTLIST_FOREACH(chunk_list, sized_chunk_t *, c, tor_free(c));
   smartlist_free(chunk_list);
 
@@ -592,9 +662,11 @@
 router_reload_router_list(void)
 {
   if (router_reload_router_list_impl(0))
-    return 1;
+    return -1;
   if (router_reload_router_list_impl(1))
-    return 1;
+    return -1;
+  if (trusted_dirs_reload_certs())
+    return -1;
   return 0;
 }
 
@@ -1155,13 +1227,13 @@
  *
  * If <b>for_exit</b>, we're picking an exit node: consider all nodes'
  * bandwidth equally regardless of their Exit status.  If not <b>for_exit</b>,
- * we're picking a non-exit node: weight exit-node's bandwidth downwards
+ * we're picking a non-exit node: weight exit-node's bandwidth less
  * depending on the smallness of the fraction of Exit-to-total bandwidth.
  */
 static void *
 smartlist_choose_by_bandwidth(smartlist_t *sl, int for_exit, int statuses)
 {
-  int i;
+  unsigned int i;
   routerinfo_t *router;
   routerstatus_t *status;
   int32_t *bandwidths;
@@ -1170,15 +1242,18 @@
   uint64_t rand_bw, tmp;
   double exit_weight;
   int n_unknown = 0;
+  bitarray_t *exit_bits;
+  int include_exits = 1;
 
   /* First count the total bandwidth weight, and make a list
    * of each value.  <0 means "unknown; no routerinfo."  We use the
    * bits of negative values to remember whether the router was fast (-x)&1
    * and whether it was an exit (-x)&2.  Yes, it's a hack. */
   bandwidths = tor_malloc(sizeof(int32_t)*smartlist_len(sl));
+  exit_bits = bitarray_init_zero(smartlist_len(sl));
 
   /* Iterate over all the routerinfo_t or routerstatus_t, and */
-  for (i = 0; i < smartlist_len(sl); ++i) {
+  for (i = 0; i < (unsigned)smartlist_len(sl); ++i) {
     /* first, learn what bandwidth we think i has */
     int is_known = 1;
     int32_t flags = 0;
@@ -1200,6 +1275,8 @@
       is_exit = router->is_exit;
       this_bw = router_get_advertised_bandwidth(router);
     }
+    if (is_exit)
+      bitarray_set(exit_bits, i);
     /* if they claim something huge, don't believe it */
     if (this_bw > MAX_BELIEVABLE_BANDWIDTH)
       this_bw = MAX_BELIEVABLE_BANDWIDTH;
@@ -1228,7 +1305,7 @@
       avg_fast = 40000;
       avg_slow = 20000;
     }
-    for (i=0; i<smartlist_len(sl); ++i) {
+    for (i=0; i<(unsigned)smartlist_len(sl); ++i) {
       int32_t bw = bandwidths[i];
       if (bw>=0)
         continue;
@@ -1252,19 +1329,28 @@
     /* If we're choosing an exit node, exit bandwidth counts fully. */
     exit_weight = 1.0;
     total_bw = total_exit_bw + total_nonexit_bw;
-  } else if (total_exit_bw < total_nonexit_bw / 2) {
-    /* If we're choosing a relay and exits are greatly outnumbered, ignore
-     * them. */
-    exit_weight = 0.0;
-    total_bw = total_nonexit_bw;
   } else {
-    /* If we're choosing a relay and exits aren't outnumbered use the formula
-     * from path-spec. */
-    uint64_t leftover = (total_exit_bw - total_nonexit_bw / 2);
-    exit_weight = U64_TO_DBL(leftover) /
-      U64_TO_DBL(leftover + total_nonexit_bw);
-    total_bw =  total_nonexit_bw +
-      DBL_TO_U64(exit_weight * U64_TO_DBL(total_exit_bw));
+    double all_bw = U64_TO_DBL(total_exit_bw+total_nonexit_bw);
+    double exit_bw = U64_TO_DBL(total_exit_bw);
+    /*
+     * For detailed derivation of this formula, see
+     *   http://archives.seul.org/or/dev/Jul-2007/msg00056.html
+     */
+    exit_weight = 1.0 - all_bw/(3.0*exit_bw);
+    if (exit_weight <= 0.0) {
+      include_exits = 0;
+      exit_weight = 0.0;
+      total_bw = total_nonexit_bw;
+    } else {
+      total_bw = 0;
+      for (i=0; i < (unsigned)smartlist_len(sl); i++) {
+        is_exit = bitarray_is_set(exit_bits, i);
+        if (is_exit)
+          total_bw += ((uint64_t)(bandwidths[i] * exit_weight));
+        else
+          total_bw += bandwidths[i];
+      }
+    }
   }
   /*
   log_debug(LD_CIRC, "Total bw = "U64_FORMAT", total exit bw = "U64_FORMAT
@@ -1279,22 +1365,26 @@
 
   /* Last, count through sl until we get to the element we picked */
   tmp = 0;
-  for (i=0; i < smartlist_len(sl); i++) {
-    if (statuses) {
-      status = smartlist_get(sl, i);
-      is_exit = status->is_exit;
-    } else {
-      router = smartlist_get(sl, i);
-      is_exit = router->is_exit;
-    }
-    if (is_exit)
-      tmp += ((uint64_t)(bandwidths[i] * exit_weight));
-    else
+  for (i=0; i < (unsigned)smartlist_len(sl); i++) {
+    is_exit = bitarray_is_set(exit_bits, i);
+    if (is_exit) {
+      if (include_exits)
+        tmp += ((uint64_t)(bandwidths[i] * exit_weight));
+    } else
       tmp += bandwidths[i];
     if (tmp >= rand_bw)
       break;
   }
+  if (i == (unsigned)smartlist_len(sl)) {
+    /* This was once possible due to round-off error, but shouldn't be able
+     * to occur any longer. */
+    tor_fragile_assert();
+    --i;
+    log_warn(LD_BUG, "Round-off error in computing bandwidth had an effect on "
+             " which router we chose.  Please tell the developers.");
+  }
   tor_free(bandwidths);
+  tor_free(exit_bits);
   return smartlist_get(sl, i);
 }
 
@@ -1815,6 +1905,7 @@
                     signed_descriptor_free(sd));
   smartlist_free(rl->routers);
   smartlist_free(rl->old_routers);
+  smartlist_free(rl->hs_dirs);
   if (routerlist->mmap_descriptors)
     tor_munmap_file(routerlist->mmap_descriptors);
   tor_free(rl);
@@ -2015,14 +2106,15 @@
  * If <b>make_old</b> is true, instead of deleting the router, we try adding
  * it to rl-&gt;old_routers. */
 void
-routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int idx, int make_old)
+routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int make_old)
 {
   routerinfo_t *ri_tmp;
   extrainfo_t *ei_tmp;
+  int idx = ri->routerlist_index;
+  tor_assert(0 <= idx && idx < smartlist_len(rl->routers));
+  tor_assert(smartlist_get(rl->routers, idx) == ri);
+
   routerlist_check_bug_417();
-  idx = _routerlist_find_elt(rl->routers, ri, idx);
-  if (idx < 0)
-    return;
   ri->routerlist_index = -1;
   smartlist_del(rl->routers, idx);
   if (idx < smartlist_len(rl->routers)) {
@@ -2074,9 +2166,9 @@
   signed_descriptor_t *sd_tmp;
   extrainfo_t *ei_tmp;
   routerlist_check_bug_417();
-  idx = _routerlist_find_elt(rl->old_routers, sd, idx);
-  if (idx < 0)
-    return;
+  tor_assert(0 <= idx && idx < smartlist_len(rl->old_routers));
+  tor_assert(smartlist_get(rl->old_routers, idx) == sd);
+
   smartlist_del(rl->old_routers, idx);
   sd_tmp = sdmap_remove(rl->desc_digest_map,
                         sd->signed_descriptor_digest);
@@ -2134,6 +2226,7 @@
     smartlist_set(rl->routers, idx, ri_new);
     ri_old->routerlist_index = -1;
     ri_new->routerlist_index = idx;
+    /* Check that ri_old is not in rl->routers anymore: */
     tor_assert( _routerlist_find_elt(rl->routers, ri_old, -1) == -1 );
   } else {
     log_warn(LD_BUG, "Appending entry from routerlist_replace.");
@@ -2330,7 +2423,10 @@
 /** Add <b>router</b> to the routerlist, if we don't already have it.  Replace
  * older entries (if any) with the same key.  Note: Callers should not hold
  * their pointers to <b>router</b> if this function fails; <b>router</b>
- * will either be inserted into the routerlist or freed.
+ * will either be inserted into the routerlist or freed. Similarly, even
+ * if this call succeeds, they should not hold their pointers to
+ * <b>router</b> after subsequent calls with other routerinfo's -- they
+ * might cause the original routerinfo to get freed.
  *
  * Returns >= 0 if the router was added; less than 0 if it was not.
  *
@@ -2360,8 +2456,11 @@
   int authdir = authdir_mode(get_options());
   int authdir_believes_valid = 0;
   routerinfo_t *old_router;
-  /* This has side effects, so do it before we start the real work */
-  int have_dir_info = router_have_minimum_dir_info();
+  /* router_have_minimum_dir_info() has side effects, so do it before we
+   * start the real work */
+  int authdir_may_warn_about_unreachable_server =
+    authdir && !from_cache && !from_fetch &&
+    router_have_minimum_dir_info();
 
   routerlist_check_bug_417();
   tor_assert(msg);
@@ -2400,7 +2499,7 @@
      * we are receiving in response to a fetch. */
 
     if (!signed_desc_digest_is_recognized(&router->cache_info) &&
-        !routerinfo_is_a_bridge(router)) {
+        !routerinfo_is_a_configured_bridge(router)) {
       /* We asked for it, so some networkstatus must have listed it when we
        * did.  Save it if we're a cache in case somebody else asks for it. */
       log_info(LD_DIR,
@@ -2456,8 +2555,7 @@
         router->num_unreachable_notifications =
           old_router->num_unreachable_notifications;
       }
-      if (authdir && !from_cache && !from_fetch &&
-          have_dir_info &&
+      if (authdir_may_warn_about_unreachable_server &&
           dirserv_thinks_router_is_blatantly_unreachable(router, time(NULL))) {
         if (router->num_unreachable_notifications >= 3) {
           unreachable = 1;
@@ -2554,8 +2652,8 @@
 routerlist_remove_old_cached_routers_with_id(time_t cutoff, int lo, int hi,
                                              digestmap_t *retain)
 {
-  int i, n = hi-lo+1, n_extra;
-  int n_rmv = 0;
+  int i, n = hi-lo+1;
+  unsigned n_extra, n_rmv = 0;
   struct duration_idx_t *lifespans;
   uint8_t *rmv, *must_keep;
   smartlist_t *lst = routerlist->old_routers;
@@ -2571,9 +2669,12 @@
 #endif
 
   /* Check whether we need to do anything at all. */
-  n_extra = n - max_descriptors_per_router();
-  if (n_extra <= 0)
-    return;
+  {
+    int mdpr = max_descriptors_per_router();
+    if (n <= mdpr)
+      return;
+    n_extra = n - mdpr;
+  }
 
   lifespans = tor_malloc_zero(sizeof(struct duration_idx_t)*n);
   rmv = tor_malloc_zero(sizeof(uint8_t)*n);
@@ -2641,6 +2742,9 @@
   routerinfo_t *router;
   signed_descriptor_t *sd;
   digestmap_t *retain;
+
+  trusted_dirs_remove_old_certs();
+
   if (!routerlist || !networkstatus_list)
     return;
 
@@ -2680,7 +2784,8 @@
         log_info(LD_DIR,
                  "Forgetting obsolete (too old) routerinfo for router '%s'",
                  router->nickname);
-        routerlist_remove(routerlist, router, i--, 1);
+        routerlist_remove(routerlist, router, 1);
+        i--;
       }
     }
   }
@@ -2854,13 +2959,13 @@
     if (purpose != ROUTER_PURPOSE_GENERAL)
       ri->cache_info.do_not_cache = 1;
 
-    if (router_add_to_routerlist(ri, &msg, from_cache, !from_cache) >= 0)
+    if (router_add_to_routerlist(ri, &msg, from_cache, !from_cache) >= 0) {
       smartlist_add(changed, ri);
+      routerlist_descriptors_added(changed);
+      smartlist_clear(changed);
+    }
   });
 
-  if (smartlist_len(changed))
-    routerlist_descriptors_added(changed);
-
   routerlist_assert_ok(routerlist);
   router_rebuild_store(0, 0);
 
@@ -3397,7 +3502,8 @@
     /* (Check whether we're currently fetching network-status objects.) */
     if (!connection_get_by_type_purpose(CONN_TYPE_DIR,
                                         DIR_PURPOSE_FETCH_NETWORKSTATUS))
-      directory_get_from_dirserver(DIR_PURPOSE_FETCH_NETWORKSTATUS,"all.z",1);
+      directory_get_from_dirserver(DIR_PURPOSE_FETCH_NETWORKSTATUS,
+                                   ROUTER_PURPOSE_GENERAL, "all.z",1);
   }
 }
 
@@ -3522,7 +3628,8 @@
         *cp++ = '+';
     });
   memcpy(cp, ".z", 3);
-  directory_get_from_dirserver(DIR_PURPOSE_FETCH_NETWORKSTATUS, resource, 1);
+  directory_get_from_dirserver(DIR_PURPOSE_FETCH_NETWORKSTATUS,
+                               ROUTER_PURPOSE_GENERAL, resource, 1);
   tor_free(resource);
   smartlist_free(missing);
 }
@@ -3534,7 +3641,7 @@
 should_delay_dir_fetches(or_options_t *options)
 {
   if (options->UseBridges && !any_bridge_descriptors_known()) {
-    log_notice(LD_DIR, "delaying dir fetches");
+    log_info(LD_DIR, "delaying dir fetches");
     return 1;
   }
   return 0;
@@ -3624,7 +3731,6 @@
       return;
     }
     hostname = tor_strdup(address);
-    a = ntohl(a);
   }
 
   ent = tor_malloc_zero(sizeof(trusted_dir_server_t));
@@ -3667,8 +3773,11 @@
 static void
 trusted_dir_server_free(trusted_dir_server_t *ds)
 {
-  if (ds->v3_cert)
-    authority_cert_free(ds->v3_cert);
+  if (ds->v3_certs) {
+    SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, cert,
+                      authority_cert_free(cert));
+    smartlist_free(ds->v3_certs);
+  }
   tor_free(ds->nickname);
   tor_free(ds->description);
   tor_free(ds->address);
@@ -3712,6 +3821,50 @@
   return NULL;
 }
 
+/** Return the most recent consensus that we have downloaded, or NULL if we
+ * don't have one. */
+networkstatus_vote_t *
+networkstatus_get_latest_consensus(void)
+{
+  return current_consensus;
+}
+
+/** Return the most recent consensus that we have downloaded, or NULL if it is
+ * no longer live. */
+networkstatus_vote_t *
+networkstatus_get_live_consensus(time_t now)
+{
+  /* XXXX020 check for liveness */
+  (void)now;
+  return current_consensus;
+}
+
+int
+networkstatus_set_current_consensus(const char *consensus)
+{
+  networkstatus_vote_t *c;
+  /* Make sure it's parseable. */
+  c = networkstatus_parse_vote_from_string(consensus, 0);
+  if (!c)
+    return -1;
+
+  /* Make sure it's signed enough. */
+  if (networkstatus_check_consensus_signature(c)<0) {
+    networkstatus_vote_free(c);
+    return -1;
+  }
+
+  if (current_consensus)
+    networkstatus_vote_free(current_consensus);
+
+  current_consensus = c;
+
+  if (get_options()->DirPort)
+    dirserv_set_cached_networkstatus_v3(consensus, c->valid_after);
+
+  return 0;
+}
+
 /** We believe networkstatuses more recent than this when they tell us that
  * our server is broken, invalid, obsolete, etc. */
 #define SELF_OPINION_INTERVAL (90*60)
@@ -4435,13 +4588,13 @@
                                             0, /* not private */
                                             resource, NULL, 0);
   } else {
-    directory_get_from_dirserver(purpose, resource, 1);
+    directory_get_from_dirserver(purpose, ROUTER_PURPOSE_GENERAL, resource, 1);
   }
   tor_free(resource);
 }
 
 /** Clients don't download any descriptor this recent, since it will probably
- * not have propageted to enough caches. */
+ * not have propagated to enough caches. */
 #define ESTIMATED_PROPAGATION_TIME (10*60)
 
 /** Return 0 if this routerstatus is obsolete, too new, isn't
@@ -4908,7 +5061,7 @@
   networkstatus_list_clean(now);
 
   if (should_delay_dir_fetches(get_options())) {
-    log_notice(LD_DIR, "no bridge descs known yet");
+    log_notice(LD_DIR, "no known bridge descriptors running yet; stalling");
     res = 0;
     goto done;
   }
@@ -5354,15 +5507,19 @@
  * THIS FUNCTION IS NOT REENTRANT.  Don't call it from outside the main
  * thread.  Also, each call invalidates the last-returned value, so don't
  * try log_warn(LD_GENERAL, "%s %s", esc_router_info(a), esc_router_info(b));
+ *
+ * If <b>router</b> is NULL, it just frees its internal memory and returns.
  */
 const char *
 esc_router_info(routerinfo_t *router)
 {
-  static char *info;
+  static char *info=NULL;
   char *esc_contact, *esc_platform;
   size_t len;
   if (info)
     tor_free(info);
+  if (!router)
+    return NULL; /* we're exiting; just free the memory we use */
 
   esc_contact = esc_for_log(router->contact_info);
   esc_platform = esc_for_log(router->platform);
@@ -5377,34 +5534,195 @@
   return info;
 }
 
-/** Determines the router that is responsible for <b>desc_id</b> and returns
- * that router's routerstatus_t.
- *
- * TODO114 At the moment we build up the router list in routerlist->hs_dirs for
- * every invocation of this function; it should be possible to maintain an
- * up-to-date router list.
- *
- * TODO114 When adding replication this function should return an array of
- * responsible routers.
+/** Return the first router that is acting as hidden service directory and that
+ * has a greater ID than <b>id</b>; if all routers have smaller IDs than
+ * <b>id</b>, return the router with the smallest ID; if the router list is
+ * NULL, or has no elements, return NULL.
  */
-routerstatus_t *
-get_responsible_hs_dir(const char *id)
+const char *
+next_hs_dir(const char *id)
 {
+  int i;
+  if (!routerlist->hs_dirs) return NULL;
+  if (routerlist->hs_dirs->num_used == 0) return NULL;
+  for (i = 0; i < routerlist->hs_dirs->num_used; i++) {
+    if (memcmp((const char*)routerlist->hs_dirs->list[i],
+               id, DIGEST_LEN) > 0) {
+      return (const char*)routerlist->hs_dirs->list[i];
+    }
+  }
+  return (const char*)routerlist->hs_dirs->list[0];
+}
+
+/** Return the first router that is acting as hidden service directory and that
+ * has a smaller ID than <b>id</b>; if all routers have greater IDs than
+ * <b>id</b>, return the router with the highest ID; if the router list is
+ * NULL, or has no elements, return NULL.
+ */
+const char *
+previous_hs_dir(const char *id)
+{
+  int i;
+  if (!routerlist->hs_dirs) return NULL;
+  if (routerlist->hs_dirs->num_used == 0) return NULL;
+  for (i = routerlist->hs_dirs->num_used - 1; i >= 0; i--) {
+    if (memcmp((const char*)routerlist->hs_dirs->list[i],
+               id, DIGEST_LEN) < 0) {
+      return (const char*)routerlist->hs_dirs->list[i];
+    }
+  }
+  return (const char*)routerlist->hs_dirs->list[
+                                           routerlist->hs_dirs->num_used - 1];
+}
+
+/** Returns any value != 0, if we are aware of enough hidden service directory
+ * to usefully perform v2 rend operations on them (publish, fetch, replicate),
+ * or 0 otherwise. */
+int
+have_enough_hs_dirs(void)
+{
+  return (routerlist->hs_dirs->num_used > NUMBER_OF_CONSECUTIVE_REPLICAS);
+}
+
+/** Determines the routers that are responsible for <b>id</b> (binary) and returns
+ * those routers' routerstatus_t. if we don't have enough HSDirs, return -1, else 0.
+ * 
+ * id is binary
+ * 
+ * hs_dirs is an array with NUMBER_OF_CONSECUTIVE_REPLICAS elements
+ */
+int
+get_responsible_hs_dirs(const char *id, routerstatus_t *hs_dirs[])
+{
   const char *digest;
-  char id_binary[20];
+  int i;
+  local_routerstatus_t *router;
+  routerstatus_t *status;
+log_warn(LD_REND, "get_responsible_hs_dirs 1");
   tor_assert(id);
-  tor_assert(strlen(id) == 32);
-  base32_decode(id_binary, 20, id, 32);
-  SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, ri,
+  if (!have_enough_hs_dirs()) {
+  	log_warn(LD_REND, "We don't have enough hidden service directories to "
+  	                  "perform v2 rendezvous operations!");
+    return -1;
+  }
+log_warn(LD_REND, "get_responsible_hs_dirs 2");
+  digest = id;
+  for (i = 0; i < NUMBER_OF_CONSECUTIVE_REPLICAS; i++) {
+log_warn(LD_REND, "get_responsible_hs_dirs 3");
+    digest = next_hs_dir(digest);
+    router = router_get_combined_status_by_digest(digest);
+    status = &(router->status);
+    hs_dirs[i] = status;
+  }
+log_warn(LD_REND, "get_responsible_hs_dirs 4");
+  return 0;
+}
+
+/** Update our hidden service routing table by iterating over the current
+ * routerstatus_list and picking all routers that have been assigned as
+ * hidden service directories by the directory authorities. */
+void
+update_hs_dir_routing_table(void)
+{
+  smartlist_t *new_hs_dirs;
+  routerstatus_t *status;
+  smartlist_t *old_hs_dirs;
+  int i;
+  int has_changed;
+  if (!routerstatus_list) {
+    log_info(LD_REND, "Cannot update hidden service routing table: "
+                      "routerstatus_list is NULL.");
+    return;
+  }
+  /* Copy the digests of all hidden service directories to a new smartlist. */
+  new_hs_dirs = smartlist_create();
+  SMARTLIST_FOREACH(routerstatus_list, local_routerstatus_t *, _local_status,
   {
-    if (ri->is_hs_dir) {
-      smartlist_add(routerlist->hs_dirs, ri->cache_info.identity_digest);
+    status = &(_local_status->status);
+    if (status->is_hs_dir) {
+      smartlist_add(new_hs_dirs, status->identity_digest);
     }
   });
-  smartlist_sort_digests(routerlist->hs_dirs);
-  digest = smartlist_digest_next_circular(routerlist->hs_dirs, id);
-  local_routerstatus_t *router = router_get_combined_status_by_digest(digest);
-  routerstatus_t *status = &(router->status);
-  return status;
+  smartlist_sort_digests(new_hs_dirs);
+  /* Has the routing table changed? */
+  has_changed = 1;
+  if (new_hs_dirs->num_used == routerlist->hs_dirs->num_used) {
+    has_changed = 0;
+    for (i=0; i < new_hs_dirs->num_used; i++) {
+      if (memcmp((const char *)new_hs_dirs->list[i],
+                 (const char *)routerlist->hs_dirs->list[i], DIGEST_LEN)) {
+        has_changed = 1;
+        break;
+      } 
+    }
+  }
+  if (has_changed) {
+  	/*log_info(LD_REND, "Hidden service routing table has changed from %d to "
+  	                  "now %d entries.",
+  	         routerlist->hs_dirs->num_used,
+  	         new_hs_dirs->num_used);*/ 
+    char *print = tor_malloc_zero(30 + new_hs_dirs->num_used * 33);
+    int c = 0;
+    char hsdir_base32[32+1];
+    SMARTLIST_FOREACH(new_hs_dirs, const char *, digest,
+    {
+      base32_encode(hsdir_base32, 32 + 1, digest, DIGEST_LEN);
+      strncpy(print + c, hsdir_base32, 32);
+      c += 32;
+      print[c++] = ' ';
+    });
+    print[c] = '\0';
+    log_info(LD_REND, "Hidden service routing table has changed from %d to "
+                      "now %d entries: %s",
+             routerlist->hs_dirs->num_used, new_hs_dirs->num_used, print);
+    tor_free(print);
+  	/* Replace old by new router list. */
+    old_hs_dirs = routerlist->hs_dirs;
+    routerlist->hs_dirs = new_hs_dirs;
+    /* At this point, we might perform operations as response to the changing
+     * routing list, e.g. if we were added/removed from this list... */
+    smartlist_free(old_hs_dirs);
+  } else {
+  	log_debug(LD_REND, "Hidden service routing table has not changed and "
+  	                   "still has %d entries",
+  	          routerlist->hs_dirs->num_used);
+    smartlist_free(new_hs_dirs);
+  }
 }
 
+/** 0 for false, 1 for true. */
+int
+acting_as_hs_dir(void)
+{
+  const char *me = router_get_my_routerinfo()->cache_info.identity_digest;
+log_info(LD_REND, "KL8 HSDir option set? %d", get_options()->HSDir);
+  if (!smartlist_digest_isin(routerlist->hs_dirs, me)) {
+    /* not acting as HS Dir */
+log_info(LD_REND, "KL8 not acting as HSDir");
+  	return 0;
+  }
+  if (routerlist->hs_dirs->num_used <= NUMBER_OF_CONSECUTIVE_REPLICAS) {
+  	/* too few HS Dirs -- that won't work */
+log_info(LD_REND, "KL8 too few HS Dirs -- that won't work");
+    return 0;
+  }
+log_info(LD_REND, "KL8 yes, acting as hs dir");
+  return 1;
+}
+
+/** 0 for false, 1 for true. */
+int
+responsible_for_desc_id(const char *query)
+{
+  const char *me;
+  const char *predecessor;
+  int i;
+  if (!acting_as_hs_dir())
+    return 0;
+  me = router_get_my_routerinfo()->cache_info.identity_digest;
+  predecessor = me;
+  for (i = 0; i <= NUMBER_OF_CONSECUTIVE_REPLICAS; i++)
+    predecessor = previous_hs_dir(predecessor);
+  return hs_dir_is_in_interval(query, predecessor, me);
+}
+

Modified: tor/branches/114-dist-storage/src/or/routerparse.c
===================================================================
--- tor/branches/114-dist-storage/src/or/routerparse.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/routerparse.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -76,6 +76,7 @@
   K_KNOWN_FLAGS,
   K_VOTE_DIGEST,
   K_CONSENSUS_DIGEST,
+  K_CONSENSUS_METHODS,
 
   R_RENDEZVOUS_SERVICE_DESCRIPTOR,
   R_VERSION,
@@ -349,6 +350,7 @@
   T1( "known-flags",         K_KNOWN_FLAGS,     CONCAT_ARGS, NO_OBJ ),
   T01("client-versions",     K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ),
   T01("server-versions",     K_SERVER_VERSIONS, CONCAT_ARGS, NO_OBJ ),
+  T1( "consensus-methods",   K_CONSENSUS_METHODS, GE(1),     NO_OBJ ),
 
   END_OF_TABLE
 };
@@ -380,6 +382,15 @@
   END_OF_TABLE
 };
 
+static token_rule_t networkstatus_detached_signature_token_table[] = {
+  T1_START("consensus-digest", K_CONSENSUS_DIGEST, GE(1),       NO_OBJ ),
+  T1("valid-after",            K_VALID_AFTER,      CONCAT_ARGS, NO_OBJ ),
+  T1("fresh-until",            K_FRESH_UNTIL,      CONCAT_ARGS, NO_OBJ ),
+  T1("valid-until",            K_VALID_UNTIL,      CONCAT_ARGS, NO_OBJ ),
+  T1N("directory-signature", K_DIRECTORY_SIGNATURE, GE(2),   NEED_OBJ ),
+  END_OF_TABLE
+};
+
 #undef T
 
 /* static function prototypes */
@@ -398,13 +409,14 @@
                            smartlist_t *out,
                            token_rule_t *table);
 static directory_token_t *get_next_token(const char **s,
+                                         const char *eos,
                                          token_rule_t *table);
 static int check_signature_token(const char *digest,
                                  directory_token_t *tok,
                                  crypto_pk_env_t *pkey,
                                  int check_authority,
                                  const char *doctype);
-static crypto_pk_env_t *find_dir_signing_key(const char *str);
+static crypto_pk_env_t *find_dir_signing_key(const char *str, const char *eos);
 static int tor_version_same_series(tor_version_t *a, tor_version_t *b);
 
 /** Set <b>digest</b> to the SHA-1 digest of the hash of the directory in
@@ -654,7 +666,7 @@
   if (tok->tp != K_DIRECTORY_SIGNATURE) {
     log_warn(LD_DIR,"Expected a single directory signature"); goto err;
   }
-  declared_key = find_dir_signing_key(str);
+  declared_key = find_dir_signing_key(str, str+strlen(str));
   note_crypto_pk_op(VERIFY_DIR);
   if (check_signature_token(digest, tok, declared_key, 1, "directory")<0)
     goto err;
@@ -715,13 +727,14 @@
   int r = -1;
   crypto_pk_env_t *declared_key = NULL;
   smartlist_t *tokens = NULL;
+  const char *eos = str + strlen(str);
 
   if (router_get_runningrouters_hash(str, digest)) {
     log_warn(LD_DIR, "Unable to compute digest of running-routers");
     goto err;
   }
   tokens = smartlist_create();
-  if (tokenize_string(str,str+strlen(str),tokens,dir_token_table)) {
+  if (tokenize_string(str,eos,tokens,dir_token_table)) {
     log_warn(LD_DIR, "Error tokenizing running-routers"); goto err;
   }
   tok = smartlist_get(tokens,0);
@@ -740,7 +753,7 @@
     log_warn(LD_DIR, "Missing signature on running-routers");
     goto err;
   }
-  declared_key = find_dir_signing_key(str);
+  declared_key = find_dir_signing_key(str, eos);
   note_crypto_pk_op(VERIFY_DIR);
   if (check_signature_token(digest, tok, declared_key, 1, "running-routers")
       < 0)
@@ -765,21 +778,23 @@
  * find the its dir-signing-key token (if any).  If this token is
  * present, extract and return the key.  Return NULL on failure. */
 static crypto_pk_env_t *
-find_dir_signing_key(const char *str)
+find_dir_signing_key(const char *str, const char *eos)
 {
   const char *cp;
   directory_token_t *tok;
   crypto_pk_env_t *key = NULL;
+  tor_assert(str);
+  tor_assert(eos);
 
   /* Is there a dir-signing-key in the directory? */
-  cp = strstr(str, "\nopt dir-signing-key");
+  cp = tor_memstr(str, eos-str, "\nopt dir-signing-key");
   if (!cp)
-    cp = strstr(str, "\ndir-signing-key");
+    cp = tor_memstr(str, eos-str, "\ndir-signing-key");
   if (!cp)
     return NULL;
   ++cp; /* Now cp points to the start of the token. */
 
-  tok = get_next_token(&cp, dir_token_table);
+  tok = get_next_token(&cp, eos, dir_token_table);
   if (!tok) {
     log_warn(LD_DIR, "Unparseable dir-signing-key token");
     return NULL;
@@ -930,7 +945,7 @@
     }
     end = tor_memstr(*s, eos-*s, "\nrouter-signature");
     if (end)
-      end = tor_memstr(end, eos-*s, "\n-----END SIGNATURE-----\n");
+      end = tor_memstr(end, eos-end, "\n-----END SIGNATURE-----\n");
     if (end)
       end += strlen("\n-----END SIGNATURE-----\n");
 
@@ -1180,7 +1195,7 @@
   }
 
   if ((tok = find_first_by_keyword(tokens, K_HIDDEN_SERVICE_DIR))) {
-    router->is_hs_dir = 1;
+    router->wants_to_be_hs_dir = 1;
   }
 
   tok = find_first_by_keyword(tokens, K_ROUTER_SIGNATURE);
@@ -1346,6 +1361,7 @@
   char *eos;
   size_t len;
   trusted_dir_server_t *ds;
+  int found;
 
   s = eat_whitespace(s);
   eos = strstr(s, "\n-----END SIGNATURE-----\n");
@@ -1380,6 +1396,8 @@
   tor_assert(tok && tok->key);
   cert->signing_key = tok->key;
   tok->key = NULL;
+  if (crypto_pk_get_digest(cert->signing_key, cert->signing_key_digest))
+    goto err;
 
   tok = find_first_by_keyword(tokens, K_DIR_IDENTITY_KEY);
   tor_assert(tok && tok->key);
@@ -1425,13 +1443,22 @@
   /* If we already have this cert, don't bother checking the signature. */
   ds = trusteddirserver_get_by_v3_auth_digest(
                                      cert->cache_info.identity_digest);
-  if (ds && ds->v3_cert &&
-      ds->v3_cert->cache_info.signed_descriptor_len == len &&
-      ds->v3_cert->cache_info.signed_descriptor_body &&
-      ! memcmp(s, ds->v3_cert->cache_info.signed_descriptor_body, len)) {
-    log_debug(LD_DIR, "We already checked the signature on this certificate;"
-              " no need to do so again.");
-  } else {
+  found = 0;
+  if (ds && ds->v3_certs) {
+    SMARTLIST_FOREACH(ds->v3_certs, authority_cert_t *, c,
+      {
+        /* XXXX020 can we just compare signed_descriptor_digest ? */
+        if (c->cache_info.signed_descriptor_len == len &&
+            c->cache_info.signed_descriptor_body &&
+            !memcmp(s, c->cache_info.signed_descriptor_body, len)) {
+          log_debug(LD_DIR, "We already checked the signature on this "
+                    "certificate; no need to do so again.");
+          found = 1;
+          break;
+        }
+      });
+  }
+  if (!found) {
     if (check_signature_token(digest, tok, cert->identity_key, 0,
                               "key certificate")) {
       goto err;
@@ -1595,7 +1622,7 @@
     } else {
       rs->version_supports_begindir =
         tor_version_as_new_as(tok->args[0], "0.1.2.2-alpha");
-      rs->version_supports_begindir =
+      rs->version_supports_extrainfo_upload =
         tor_version_as_new_as(tok->args[0], "0.2.0.0-alpha-dev (r10070)");
     }
     if (vote_rs) {
@@ -1808,7 +1835,9 @@
   return ns;
 }
 
-/** DOCDOC */
+/** Parse a v3 networkstatus vote (if <b>is_vote</b> is true) or a v3
+ * networkstatus consensus (if <b>is_vote</b> is false) from <b>s</b>, and
+ * return the result.  Return NULL on failure. */
 networkstatus_vote_t *
 networkstatus_parse_vote_from_string(const char *s, int is_vote)
 {
@@ -2088,9 +2117,8 @@
         goto err;
       v->good_signature = 1;
     } else {
-      v->pending_signature = tor_memdup(tok->object_body,
-                                        tok->object_size);
-      v->pending_signature_len = tok->object_size;
+      v->signature = tor_memdup(tok->object_body, tok->object_size);
+      v->signature_len = tok->object_size;
     }
   });
   /* XXXX020 enforce: vote must have at least one signature. */
@@ -2125,6 +2153,110 @@
   return ns;
 }
 
+/** Parse a detached v3 networkstatus signature document between <b>s</b> and
+ * <b>eos</b> and return the result.  Return -1 on failure. */
+ns_detached_signatures_t *
+networkstatus_parse_detached_signatures(const char *s, const char *eos)
+{
+  /* XXXX020 there is too much duplicate code here. */
+  directory_token_t *tok;
+
+  smartlist_t *tokens = smartlist_create();
+  ns_detached_signatures_t *sigs =
+    tor_malloc_zero(sizeof(ns_detached_signatures_t));
+
+  if (!eos)
+    eos = s + strlen(s);
+
+  if (tokenize_string(s, eos, tokens,
+                      networkstatus_detached_signature_token_table)) {
+    log_warn(LD_DIR, "Error tokenizing detached networkstatus signatures");
+    goto err;
+  }
+
+  tok = find_first_by_keyword(tokens, K_CONSENSUS_DIGEST);
+  if (strlen(tok->args[0]) != HEX_DIGEST_LEN) {
+    log_warn(LD_DIR, "Wrong length on consensus-digest in detached "
+             "networkstatus signatures");
+    goto err;
+  }
+  if (base16_decode(sigs->networkstatus_digest, DIGEST_LEN,
+                    tok->args[0], strlen(tok->args[0])) < 0) {
+    log_warn(LD_DIR, "Bad encoding on on consensus-digest in detached "
+             "networkstatus signatures");
+    goto err;
+  }
+
+  tok = find_first_by_keyword(tokens, K_VALID_AFTER);
+  if (parse_iso_time(tok->args[0], &sigs->valid_after)) {
+    log_warn(LD_DIR, "Bad valid-after in detached networkstatus signatures");
+    goto err;
+  }
+
+  tok = find_first_by_keyword(tokens, K_FRESH_UNTIL);
+  if (parse_iso_time(tok->args[0], &sigs->fresh_until)) {
+    log_warn(LD_DIR, "Bad fresh-until in detached networkstatus signatures");
+    goto err;
+  }
+
+  tok = find_first_by_keyword(tokens, K_VALID_UNTIL);
+  if (parse_iso_time(tok->args[0], &sigs->valid_until)) {
+    log_warn(LD_DIR, "Bad valid-until in detached networkstatus signatures");
+    goto err;
+  }
+
+  sigs->signatures = smartlist_create();
+  SMARTLIST_FOREACH(tokens, directory_token_t *, _tok,
+    {
+      char id_digest[DIGEST_LEN];
+      char sk_digest[DIGEST_LEN];
+      networkstatus_voter_info_t *voter;
+
+      tok = _tok;
+      if (tok->tp != K_DIRECTORY_SIGNATURE)
+        continue;
+      tor_assert(tok->n_args >= 2);
+
+      if (!tok->object_type ||
+          strcmp(tok->object_type, "SIGNATURE") ||
+          tok->object_size < 128 || tok->object_size > 512) {
+        log_warn(LD_DIR, "Bad object type or length on directory-signature");
+        goto err;
+      }
+
+      if (strlen(tok->args[0]) != HEX_DIGEST_LEN ||
+          base16_decode(id_digest, sizeof(id_digest),
+                        tok->args[0], HEX_DIGEST_LEN) < 0) {
+        log_warn(LD_DIR, "Error decoding declared identity %s in "
+                 "network-status vote.", escaped(tok->args[0]));
+        goto err;
+      }
+      if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
+          base16_decode(sk_digest, sizeof(sk_digest),
+                        tok->args[1], HEX_DIGEST_LEN) < 0) {
+        log_warn(LD_DIR, "Error decoding declared identity %s in "
+                 "network-status vote.", escaped(tok->args[1]));
+        goto err;
+      }
+
+      voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
+      memcpy(voter->identity_digest, id_digest, DIGEST_LEN);
+      memcpy(voter->signing_key_digest, sk_digest, DIGEST_LEN);
+      voter->signature = tor_memdup(tok->object_body, tok->object_size);
+      voter->signature_len = tok->object_size;
+
+      smartlist_add(sigs->signatures, voter);
+    });
+
+  goto done;
+ err:
+  ns_detached_signatures_free(sigs);
+  sigs = NULL;
+ done:
+  SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+  return sigs;
+}
+
 /** Parse the addr policy in the string <b>s</b> and return it.  If
  * assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or
  * ADDR_POLICY_REJECT) for items that specify no action.
@@ -2134,9 +2266,10 @@
 {
   directory_token_t *tok = NULL;
   const char *cp;
-  char *tmp;
+  char *tmp = NULL;
   addr_policy_t *r;
   size_t len, idx;
+  const char *eos;
 
   /* *s might not end with \n, so we need to extend it with one. */
   len = strlen(s);
@@ -2155,7 +2288,8 @@
     tor_free(tmp);
     cp = tmp = new_str;
   }
-  tok = get_next_token(&cp, routerdesc_token_table);
+  eos = cp + strlen(cp);
+  tok = get_next_token(&cp, eos, routerdesc_token_table);
   if (tok->tp == _ERR) {
     log_warn(LD_DIR, "Error reading address policy: %s", tok->error);
     goto err;
@@ -2215,7 +2349,7 @@
   newe->policy_type = (tok->tp == K_REJECT) ? ADDR_POLICY_REJECT
     : ADDR_POLICY_ACCEPT;
 
-  if (parse_addr_and_port_range(arg, &newe->addr, &newe->msk,
+  if (parse_addr_and_port_range(arg, &newe->addr, &newe->maskbits,
                                 &newe->prt_min, &newe->prt_max))
     goto policy_read_failed;
 
@@ -2269,7 +2403,7 @@
                  tok->tp == K_REJECT ? "reject" : "accept",
                  private_nets[net], arg);
     if (parse_addr_and_port_range((*nextp)->string + 7,
-                                  &(*nextp)->addr, &(*nextp)->msk,
+                                  &(*nextp)->addr, &(*nextp)->maskbits,
                                   &(*nextp)->prt_min, &(*nextp)->prt_max)) {
       log_warn(LD_BUG, "Couldn't parse an address range we generated!");
       return NULL;
@@ -2293,7 +2427,7 @@
     tor_assert(t2);
     tor_assert(t2->policy_type == t->policy_type);
     tor_assert(t2->addr == t->addr);
-    tor_assert(t2->msk == t->msk);
+    tor_assert(t2->maskbits == t->maskbits);
     tor_assert(t2->prt_min == t->prt_min);
     tor_assert(t2->prt_max == t->prt_max);
     tor_assert(!strcmp(t2->string, t->string));
@@ -2385,10 +2519,10 @@
  * of tokens in <b>table</b>.
  */
 static directory_token_t *
-get_next_token(const char **s, token_rule_t *table)
+get_next_token(const char **s, const char *eos, token_rule_t *table)
 {
-  const char *next, *obstart;
-  int i, j, done, allocated;
+  const char *next, *eol, *obstart;
+  int i, j, allocated, obname_len;
   directory_token_t *tok;
   obj_syntax o_syn = NO_OBJ;
   char ebuf[128];
@@ -2406,63 +2540,55 @@
   tok = tor_malloc_zero(sizeof(directory_token_t));
   tok->tp = _ERR;
 
-  *s = eat_whitespace(*s);
-  if (!**s) {
-    tok->tp = _EOF;
-    return tok;
-  }
-  next = find_whitespace(*s);
-  if (!next) {
-    tok->error = tor_strdup("Unexpected EOF"); return tok;
-  }
-  /* It's a keyword... but which one? */
-  if (!strncmp("opt", *s, next-*s)) {
+  /* Set *s to first token, eol to end-of-line, next to after first token */
+  *s = eat_whitespace_eos(*s, eos); /* eat multi-line whitespace */
+  eol = memchr(*s, '\n', eos-*s);
+  if (!eol)
+    eol = eos;
+  next = find_whitespace_eos(*s, eol);
+
+  if (!strcmp_len(*s, "opt", next-*s)) {
     /* Skip past an "opt" at the start of the line. */
-    *s = eat_whitespace(next);
-    next = NULL;
-    if (**s)
-      next = find_whitespace(*s);
-    if (!**s || !next) {
-      RET_ERR("opt without keyword");
-    }
+    *s = eat_whitespace_eos_no_nl(next, eol);
+    next = find_whitespace_eos(*s, eol);
+  } else if (*s == eos) {  /* If no "opt", and end-of-line, line is invalid */
+    RET_ERR("Unexpected EOF");
   }
+
   /* Search the table for the appropriate entry.  (I tried a binary search
    * instead, but it wasn't any faster.) */
   for (i = 0; table[i].t ; ++i) {
-    if (!strncmp(table[i].t, *s, next-*s)) {
+    if (!strcmp_len(*s, table[i].t, next-*s)) {
       /* We've found the keyword. */
       kwd = table[i].t;
       tok->tp = table[i].v;
       o_syn = table[i].os;
-      if (table[i].concat_args) {
-        /* The keyword takes the line as a single argument */
-        *s = eat_whitespace_no_nl(next);
-        next = strchr(*s, '\n');
-        if (!next)
-          RET_ERR("Unexpected EOF");
-        tok->args = tor_malloc(sizeof(char*));
-        tok->args[0] = tor_strndup(*s,next-*s);
-        tok->n_args = 1;
-        *s = eat_whitespace_no_nl(next+1);
-      } else {
-        /* This keyword takes multiple arguments. */
-        j = 0;
-        done = (*next == '\n');
-        allocated = 32;
-        tok->args = tor_malloc(sizeof(char*)*32);
-        *s = eat_whitespace_no_nl(next);
-        while (**s != '\n' && !done) {
-          next = find_whitespace(*s);
-          if (*next == '\n')
-            done = 1;
-          if (j == allocated) {
-            allocated *= 2;
-            tok->args = tor_realloc(tok->args,sizeof(char*)*allocated);
+      *s = eat_whitespace_eos_no_nl(next, eol);
+      next = find_whitespace_eos(*s, eol);
+      if (1 || *s < eol) { /* make sure there are arguments to store */
+        /* XXXX020 actually, we go ahead whether there are arguments or not,
+         * so that tok->args is always set if we want arguments. */
+        if (table[i].concat_args) {
+          /* The keyword takes the line as a single argument */
+          tok->args = tor_malloc(sizeof(char*));
+          tok->args[0] = tor_strndup(*s,eol-*s); /* Grab everything on line */
+          tok->n_args = 1;
+        } else {
+          /* This keyword takes multiple arguments. */
+          j = 0;
+          allocated = 16;
+          tok->args = tor_malloc(sizeof(char*)*allocated);
+          while (*s < eol) { /* While not at eol, store the next token */
+            if (j == allocated) {
+              allocated *= 2;
+              tok->args = tor_realloc(tok->args,sizeof(char*)*allocated);
+            }
+            tok->args[j++] = tor_strndup(*s, next-*s);
+            *s = eat_whitespace_eos_no_nl(next, eol); /* eat intra-line ws */
+            next = find_whitespace_eos(*s, eol); /* find end of token at *s */
           }
-          tok->args[j++] = tor_strndup(*s,next-*s);
-          *s = eat_whitespace_no_nl(next+1);
+          tok->n_args = j;
         }
-        tok->n_args = j;
       }
       if (tok->n_args < table[i].min_args) {
         tor_snprintf(ebuf, sizeof(ebuf), "Too few arguments to %s", kwd);
@@ -2471,60 +2597,63 @@
         tor_snprintf(ebuf, sizeof(ebuf), "Too many arguments to %s", kwd);
         RET_ERR(ebuf);
       }
-
       break;
     }
   }
-  if (tok->tp == _ERR) {
+
+  if (tok->tp == _ERR) { /* No keyword matched; call it an "opt". */
     tok->tp = K_OPT;
-    *s = eat_whitespace_no_nl(next);
-    next = strchr(*s,'\n');
-    if (!next)
-      RET_ERR("Unexpected EOF");
     tok->args = tor_malloc(sizeof(char*));
-    tok->args[0] = tor_strndup(*s,next-*s);
+    tok->args[0] = tor_strndup(*s, eol-*s);
     tok->n_args = 1;
-    *s = eat_whitespace_no_nl(next+1);
     o_syn = OBJ_OK;
   }
-  *s = eat_whitespace(*s);
-  if (strcmpstart(*s, "-----BEGIN ")) {
+
+  /* Check whether there's an object present */
+  *s = eat_whitespace_eos(eol, eos);  /* Scan from end of first line */
+  eol = memchr(*s, '\n', eos-*s);
+  if (!eol || eol-*s<11 || strcmpstart(*s, "-----BEGIN ")) /* No object. */
     goto check_object;
-  }
-  obstart = *s;
-  *s += 11; /* length of "-----BEGIN ". */
-  next = strchr(*s, '\n');
-  if (next-*s < 6 || strcmpstart(next-5, "-----\n")) {
+
+  obstart = *s; /* Set obstart to start of object spec */
+  if (*s+11 >= eol-5 || memchr(*s+11,'\0',eol-*s-16) || /* no short lines, */
+      strcmp_len(eol-5, "-----", 5)) {          /* nuls or invalid endings */
     RET_ERR("Malformed object: bad begin line");
   }
-  tok->object_type = tor_strndup(*s, next-*s-5);
-  *s = next+1;
-  next = strstr(*s, "-----END ");
+  tok->object_type = tor_strndup(*s+11, eol-*s-16);
+  obname_len = eol-*s-16; /* store objname length here to avoid a strlen() */
+  *s = eol+1;    /* Set *s to possible start of object data (could be eos) */
+
+  /* Go to the end of the object */
+  next = tor_memstr(*s, eos-*s, "-----END ");
   if (!next) {
-    RET_ERR("Malformed object: missing end line");
+    RET_ERR("Malformed object: missing object end line");
   }
-  if (!strcmp(tok->object_type, "RSA PUBLIC KEY")) {
-    if (strcmpstart(next, "-----END RSA PUBLIC KEY-----\n"))
-      RET_ERR("Malformed object: mismatched end line");
-    next = strchr(next,'\n')+1;
+  eol = memchr(next, '\n', eos-next);
+  if (!eol)  /* end-of-line marker, or eos if there's no '\n' */
+    eol = eos;
+  /* Validate the ending tag, which should be 9 + NAME + 5 + eol */
+  if (eol-next != 9+obname_len+5 ||
+      strcmp_len(next+9, tok->object_type, obname_len) ||
+      strcmp_len(eol-5, "-----", 5)) {
+    snprintf(ebuf, sizeof(ebuf), "Malformed object: mismatched end tag %s",
+             tok->object_type);
+    ebuf[sizeof(ebuf)-1] = '\0';
+    RET_ERR(ebuf);
+  }
+  if (!strcmp(tok->object_type, "RSA PUBLIC KEY")) { /* If it's a key... */
     tok->key = crypto_new_pk_env();
-    if (crypto_pk_read_public_key_from_string(tok->key, obstart, next-obstart))
+    if (crypto_pk_read_public_key_from_string(tok->key, obstart, eol-obstart))
       RET_ERR("Couldn't parse public key.");
-    *s = next;
-  } else {
+  } else { /* If it's something else, try to base64-decode it */
+    int r;
     tok->object_body = tor_malloc(next-*s); /* really, this is too much RAM. */
-    i = base64_decode(tok->object_body, next-*s, *s, next-*s);
-    if (i<0) {
+    r = base64_decode(tok->object_body, next-*s, *s, next-*s);
+    if (r<0)
       RET_ERR("Malformed object: bad base64-encoded data");
-    }
-    tok->object_size = i;
-    *s = next + 9; /* length of "-----END ". */
-    i = strlen(tok->object_type);
-    if (strncmp(*s, tok->object_type, i) || strcmpstart(*s+i, "-----\n")) {
-      RET_ERR("Malformed object: mismatched end tag");
-    }
-    *s += i+6;
+    tok->object_size = r;
   }
+  *s = eol;
 
  check_object:
   tok = token_check_object(kwd, tok, o_syn);
@@ -2553,14 +2682,14 @@
   for (i = 0; i < _NIL; ++i)
     counts[i] = 0;
   while (*s < end && (!tok || tok->tp != _EOF)) {
-    tok = get_next_token(s, table);
+    tok = get_next_token(s, end, table);
     if (tok->tp == _ERR) {
       log_warn(LD_DIR, "parse error: %s", tok->error);
       return -1;
     }
     ++counts[tok->tp];
     smartlist_add(out, tok);
-    *s = eat_whitespace(*s);
+    *s = eat_whitespace_eos(*s, end);
   }
 
   for (i = 0; table[i].t; ++i) {
@@ -2852,234 +2981,260 @@
     smartlist_uniq(versions, _compare_tor_version_str_ptr, NULL);
 }
 
-/* Parse encoded descriptor; TODO add possibility to parse multiple descs from
- * one string for replication purposes! */
+/** Parse and validate the ASCII-encoded v2 descriptor in <b>desc</b>, write
+ * the parsed descriptor to (the newly allocated) <b>parsed</b>, the binary
+ * descriptor ID to <b>desc_id</b>, the encrypted introduction points to (the
+ * newly allocated) <b>intro_points_encrypted</b>, their encrypted size to
+ * <b>intro_points_encrypted_size</b>, and advance to the possibly next
+ * descriptor if <b>eat_desc</b> is 1; return 0 for success (including
+ * descriptor validation) or -1 for failure.
+ */
 int
-rend_parse_v2_service_descriptor(rend_service_descriptor_t *result,
-                                 const char *s)
+rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed,
+                                 char *desc_id, char **intro_points_encrypted,
+                                 size_t *intro_points_encrypted_size,
+                                 const char **desc, int eat_desc)
 {
-
-  const char *end;
-  char digesthash[128];
+  rend_service_descriptor_t *result =
+                            tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  char desc_hash[DIGEST_LEN];
+  const char *eos;
+  smartlist_t *tokens = smartlist_create();
+  directory_token_t *tok;
+  char secret_id_part[DIGEST_LEN];
   int protocols;
-  smartlist_t *tokens = NULL;
-  directory_token_t *tok;
-  //uint8_t version;
-
-  /* point 'end' to a point immediately after the final newline. */
-  end = s + strlen(s);
-  while (end > s+2 && *(end-1) == '\n' && *(end-2) == '\n')
-    --end;
-
-  /* compute descriptor hash */
-  if (router_get_hash_impl(s,digesthash, "rendezvous-service-descriptor ",
+  char public_key_hash[DIGEST_LEN];
+  char test_desc_id[DIGEST_LEN];
+  crypto_digest_env_t *digest;
+  tor_assert(*desc);
+  /* Check if desc starts correctly. */
+  if (strncmp(*desc, "rendezvous-service-descriptor ",
+              strlen("rendezvous-service-descriptor "))) {
+    log_info(LD_REND, "Descriptor does not start correctly.");
+    goto err;
+  }
+  /* Compute descriptor hash for later validation. */
+  if (router_get_hash_impl(*desc, desc_hash, "rendezvous-service-descriptor ",
                            "\nsignature", '\n') < 0) {
-    log_warn(LD_DIR, "Couldn't compute descriptor hash.");
-    return -1;
+    log_warn(LD_REND, "Couldn't compute descriptor hash.");
+    goto err;
   }
-
-  /* tokenize descriptor */
-  tokens = smartlist_create();
-  if (tokenize_string(s,end,tokens,desc_token_table)) {
-    log_warn(LD_DIR, "Error tokenizing descriptor.");
+  /* Determine end of string. */
+  eos = strstr(*desc, "\nrendezvous-service-descriptor ");
+  if (!eos)
+    eos = *desc + strlen(*desc);
+  else
+    eos = eos + 1;
+  /* Tokenize descriptor. */
+  if (tokenize_string(*desc, eos, tokens, desc_token_table)) {
+    log_warn(LD_REND, "Error tokenizing descriptor.");
     goto err;
   }
-
-  /* check min allowed length of token list */
+  /* If requested, set desc to next descriptor, if available. */
+  if (eat_desc)
+    *desc = eos;
+  /* Check min allowed length of token list. */
   if (smartlist_len(tokens) < 8) {
-    log_warn(LD_DIR, "Impossibly short descriptor.");
+    log_warn(LD_REND, "Impossibly short descriptor.");
     goto err;
   }
-
-  /* check whether descriptor starts correctly */
-  tok = smartlist_get(tokens,0);
+  /* Check whether descriptor starts correctly. */
+  tok = smartlist_get(tokens, 0);
   if (tok->tp != R_RENDEZVOUS_SERVICE_DESCRIPTOR) {
-    log_warn(LD_DIR,"Entry does not start with "
+    log_warn(LD_REND, "Entry does not start with "
              "\"rendezvous-service-descriptor\"");
     goto err;
   }
-
-  /* parse publication time; TODO check if up-to-date */
-  tok = find_first_by_keyword(tokens, R_PUBLICATION_TIME);
+  /* Parse base32-encoded descriptor ID. */
+  tok = find_first_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR);
   tor_assert(tok);
   tor_assert(tok->n_args == 1);
-  if (parse_iso_time(tok->args[0], &result->timestamp) < 0)
+  if (strlen(tok->args[0]) != 32 ||
+      strspn(tok->args[0], BASE32_CHARS) != 32) {
+    log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]);
     goto err;
-
-  /* parse descriptor version */
+  }
+  base32_decode(desc_id, DIGEST_LEN, tok->args[0], REND_DESC_ID_V2_LEN);
+  /* Parse descriptor version. */
   tok = find_first_by_keyword(tokens, R_VERSION);
+  tor_assert(tok);
+  tor_assert(tok->n_args == 1);
   result->version = atoi(tok->args[0]);
-  if (result->version != 2) {
-    log_warn(LD_DIR, "wrong descriptor version: %d", result->version);
+  if (result->version < 2) {
+    log_warn(LD_REND, "Wrong descriptor version: %d", result->version);
     goto err;
   }
-
-  /* parse public key */
+  /* Parse public key. */
   tok = find_first_by_keyword(tokens, R_PERMANENT_KEY);
   tor_assert(tok);
   result->pk = tok->key;
   tok->key = NULL; /* Prevent free */
-
-  /* parse secret ID part */
+  /* Parse secret ID part. */
   tok = find_first_by_keyword(tokens, R_SECRET_ID_PART);
+  tor_assert(tok);
   tor_assert(tok->n_args == 1);
-  strlcpy(result->secret_id_part[0], tok->args[0],
-          sizeof(result->secret_id_part[0]));
-
-  /* parse descriptor ID */
-  tok = find_first_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR);
+  if (strlen(tok->args[0]) != 32 ||
+      strspn(tok->args[0], BASE32_CHARS) != 32) {
+    log_warn(LD_REND, "Invalid secret ID part: '%s'", tok->args[0]);
+    goto err;
+  }
+  base32_decode(secret_id_part, DIGEST_LEN, tok->args[0], 32);
+  /* Parse publication time -- up-to-date check is done later. */
+  tok = find_first_by_keyword(tokens, R_PUBLICATION_TIME);
+  tor_assert(tok);
   tor_assert(tok->n_args == 1);
-  strlcpy(result->desc_id[0], tok->args[0], sizeof(result->desc_id[0]));
-
-  /* parse encrypted introduction points */
-  tok = find_first_by_keyword(tokens, R_INTRODUCTION_POINTS);
-  result->intro_points_encrypted = tok->object_body;
-  tok->object_body = NULL; /* Prevent free */
-  result->intro_points_encrypted_size = tok->object_size;
-
-  /* parse protocol versions */
+  if (parse_iso_time(tok->args[0], &result->timestamp) < 0) {
+  	log_warn(LD_REND, "Invalid publication time: '%s'", tok->args[0]);
+    goto err;
+  }
+  /* Parse protocol versions. */
   tok = find_first_by_keyword(tokens, R_PROTOCOL_VERSIONS);
+  tor_assert(tok);
+  tor_assert(tok->n_args == 1);
   protocols = atoi(tok->args[0]);
   result->protocols = protocols;
-
-  /* parse and verify signature */
+  /* Parse encrypted introduction points. Don't verify. */
+  tok = find_first_by_keyword(tokens, R_INTRODUCTION_POINTS);
+  tor_assert(tok);
+  *intro_points_encrypted = tok->object_body;
+  tok->object_body = NULL; /* Prevent free */
+  *intro_points_encrypted_size = tok->object_size;
+  /* Parse and verify signature. */
   tok = find_first_by_keyword(tokens, R_SIGNATURE);
   tor_assert(tok);
   note_crypto_pk_op(VERIFY_RTR);
-  if (check_signature_token(digesthash, tok, result->pk, 0,
-                            "rendezvous service descriptor") < 0)
+  if (check_signature_token(desc_hash, tok, result->pk, 0,
+                            "v2 rendezvous service descriptor") < 0)
     goto err;
-
+  /* Verify that descriptor ID belongs to public key and secret ID part. */
+  crypto_pk_get_digest(result->pk, public_key_hash);
+  digest = crypto_new_digest_env();
+  crypto_digest_add_bytes(digest, public_key_hash, 10);
+  crypto_digest_add_bytes(digest, secret_id_part, 20);
+  crypto_digest_get_digest(digest, test_desc_id, 20);
+  crypto_free_digest_env(digest);
+  if (memcmp(desc_id, test_desc_id, DIGEST_LEN)) {
+    log_warn(LD_REND, "Parsed descriptor ID does not match "
+             "computed descriptor ID.");
+    goto err;
+  }
   goto done;
-
  err:
-  /* TODO free some memory */
-
+  if (result)
+    rend_service_descriptor_free(result);
+  result = NULL;
  done:
-  /* TODO free smartlists and stuff */
-
-  return 0;
+  if (tokens) {
+  	SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+  	smartlist_free(tokens);
+  }
+  *parsed = result;
+  if (result)
+    return 0;
+  return -1;
 }
 
+/** Decrypt the introduction points in <b>intro_points_encrypted</b> of length
+ * <b>intro_points_encrypted_size</b> using the base32-encoded
+ * <b>secret_cookie</b>, parse the introduction points, and write the result
+ * to <b>parsed</b>; return the number of successfully parsed introduction
+ * points.
+ */ 
 int
 rend_decrypt_introduction_points(rend_service_descriptor_t *parsed,
-                                 const char *secret_cookie)
+                                 const char *secret_cookie,
+                                 char *intro_points_encrypted,
+                                 size_t intro_points_encrypted_size)
 {
-  smartlist_t *intropoints; /* smartlist for parsed intro points */
-  char key[16]; /* decryption key */
-  char *ipos_decrypted; /* decrypted intro points */
-  int unenclen; /* length of unencrypted intro points */
-  smartlist_t *tokens; /* smartlist for intro point tokens */
-  const char **s2; /* currently investigated intro point string */
+  char *ipos_decrypted;
+  char key[16];
+  int unenclen;
+  const char **current_ipo;
+  smartlist_t *intropoints;
+  smartlist_t *tokens;
+  const char *eos;
   int i;
-
-tor_assert(secret_cookie);
-
-  /* create smartlists */
-  tokens = smartlist_create();
-  intropoints = smartlist_create();
-
-  /* decrypt introduction points and store info in parsed */
-  ipos_decrypted = tor_malloc_zero(parsed->intro_points_encrypted_size);
+  directory_token_t *tok;
+  extend_info_t *info;
+  struct in_addr ip;
+  tor_assert(parsed);
+  tor_assert(secret_cookie);
+  tor_assert(strlen(secret_cookie) == 24);
+  tor_assert(intro_points_encrypted);
+  tor_assert(intro_points_encrypted_size > 0);
+  /* Decrypt introduction points. */
+  ipos_decrypted = tor_malloc_zero(intro_points_encrypted_size + 32);
   base32_decode(key, 15, secret_cookie, 24);
-  //memcpy(key, secret_cookie, 15);
   key[15] = 0;
   unenclen = crypto_cipher_decrypt_cbc(key, ipos_decrypted,
-                                       parsed->intro_points_encrypted,
-                                       parsed->intro_points_encrypted_size);
-
-  /* consider one intro point after the other */
-  s2 = (const char **)&ipos_decrypted;
-  while (!strcmpstart(*s2, "introduction-point ")) {
-
-    const char *eos; /* end position of string */
-    directory_token_t *tok; /* single token */
-    extend_info_t *info; /* extend info to write parsed result to */
-    struct in_addr in; /* IP address */
-
-    /* determine end of string */
-    eos = strstr(*s2, "\nintroduction-point ");
+                                       intro_points_encrypted,
+                                       intro_points_encrypted_size);
+  /* Consider one intro point after the other. */
+  current_ipo = (const char **)&ipos_decrypted;
+  intropoints = smartlist_create();
+  tokens = smartlist_create();
+  while (!strcmpstart(*current_ipo, "introduction-point ")) {
+    /* Determine end of string. */
+    eos = strstr(*current_ipo, "\nintroduction-point ");
     if (!eos)
-      eos = *s2+strlen(*s2);
+      eos = *current_ipo+strlen(*current_ipo);
     else
       eos = eos+1;
-
-    /* free old tokens, if available, and clear token list */
-    SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
-    smartlist_clear(tokens);
-
-    /* tokenize string */
-    if (tokenize_string(*s2, eos, tokens, ipo_token_table)) {
-      log_warn(LD_DIR, "Error tokenizing introduction point");
-      goto err;
+    /* Tokenize string. */
+    if (tokenize_string(*current_ipo, eos, tokens, ipo_token_table)) {
+      log_warn(LD_REND, "Error tokenizing introduction point.");
+      break;
     }
-
-    /* check minimum allowed length of introduction point */
+    /* Advance to next introduction point, if available. */
+    *current_ipo = eos;
+    /* Check minimum allowed length of introduction point. */
     if (smartlist_len(tokens) < 5) {
-      log_warn(LD_DIR, "Impossibly short introduction point");
-      goto err;
+      log_warn(LD_REND, "Impossibly short introduction point.");
+      break;
     }
-
-    /* allocate new extend info */
+    /* Allocate new extend info. */
     info = tor_malloc_zero(sizeof(extend_info_t));
-
-    /* parse identifier */
+    /* Parse identifier. */
     tok = find_first_by_keyword(tokens, R_IPO_IDENTIFIER);
     tor_assert(tok);
-
-    /* base32-decode identifier and store it */
     base32_decode(info->identity_digest, DIGEST_LEN, tok->args[0], 32);
-
-    /* write identifier to nickname */
+    /* Write identifier to nickname. */
     info->nickname[0] = '$';
-    base16_encode(info->nickname+1, sizeof(info->nickname)-1,
+    base16_encode(info->nickname + 1, sizeof(info->nickname) - 1,
                   info->identity_digest, DIGEST_LEN);
-
-    /* parse IP address */
+    /* Parse IP address. */
     tok = find_first_by_keyword(tokens, R_IPO_IP_ADDRESS);
-    if (tor_inet_aton(tok->args[0], &in) != 0) {
-      info->addr = ntohl(in.s_addr);
-    } else {
-      log_warn(LD_DIR, "could not parse IP address");
+    if (tor_inet_aton(tok->args[0], &ip) == 0) {
+      log_warn(LD_REND, "Could not parse IP address.");
+      tor_free(info);
+      break;
     }
-
-    /* parse onion port */
+    info->addr = ntohl(ip.s_addr);
+    /* Parse onion port. */
     tok = find_first_by_keyword(tokens, R_IPO_ONION_PORT);
     info->port = (uint16_t) atoi(tok->args[0]);
-
-    /* parse onion key */
+    /* Parse onion key. */
     tok = find_first_by_keyword(tokens, R_IPO_ONION_KEY);
     info->onion_key = tok->key;
     tok->key = NULL; /* Prevent free */
-
-    /* add extend info to list of introduction points */
+    /* TODO114 parse service key. */
+    /* Add extend info to list of introduction points. */
     smartlist_add(intropoints, info);
-
-    /* advance to next introduction point, if available */
-    *s2 = eos;
+    /* Free tokens and clear token list. */
+    SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
+    smartlist_clear(tokens);
   }
-
-  /* write extend infos to descriptor */
+  /* Write extend infos to descriptor. */
   parsed->n_intro_points = smartlist_len(intropoints);
   parsed->intro_point_extend_info =
-    tor_malloc_zero(sizeof(extend_info_t*) * parsed->n_intro_points);
+    tor_malloc_zero(sizeof(extend_info_t *) * parsed->n_intro_points);
   parsed->intro_points =
-    tor_malloc_zero(sizeof(char*) * parsed->n_intro_points);
-
+    tor_malloc_zero(sizeof(char *) * parsed->n_intro_points);
   i = 0;
   SMARTLIST_FOREACH(intropoints, extend_info_t *, ipo, {
       parsed->intro_points[i] = tor_strdup(ipo->nickname);
       parsed->intro_point_extend_info[i++] = ipo;
-    });
-
-  goto done;
-
- err:
-  /* TODO free some memory */
-
- done:
-  /* TODO free smartlists and stuff */
-
-  return 0;
+  });
+  return parsed->n_intro_points;
 }
 

Modified: tor/branches/114-dist-storage/src/or/test.c
===================================================================
--- tor/branches/114-dist-storage/src/or/test.c	2007-08-11 15:39:12 UTC (rev 11078)
+++ tor/branches/114-dist-storage/src/or/test.c	2007-08-11 22:30:44 UTC (rev 11079)
@@ -36,9 +36,9 @@
 #define ROUTER_PRIVATE
 
 #include "or.h"
-#include "../common/test.h"
-#include "../common/torgzip.h"
-#include "../common/mempool.h"
+#include "test.h"
+#include "torgzip.h"
+#include "mempool.h"
 
 int have_failed = 0;
 
@@ -99,7 +99,7 @@
 static crypto_pk_env_t *
 pk_generate(int idx)
 {
-  static crypto_pk_env_t *pregen[3] = {NULL, NULL, NULL};
+  static crypto_pk_env_t *pregen[5] = {NULL, NULL, NULL, NULL, NULL};
   tor_assert(idx < (int)(sizeof(pregen)/sizeof(pregen[0])));
   if (! pregen[idx]) {
     pregen[idx] = crypto_new_pk_env();
@@ -854,13 +854,14 @@
   buf[0] = (char)1;
   test_assert(!tor_mem_is_zero(buf, 10));
 
-  /* Test inet_ntoa */
+  /* Test inet_ntop */
   {
-    char tmpbuf[INET_NTOA_BUF_LEN];
+    char tmpbuf[TOR_ADDR_BUF_LEN];
+    const char *ip = "176.192.208.224";
     struct in_addr in;
-    tor_inet_aton("18.244.0.188",&in);
-    tor_inet_ntoa(&in, tmpbuf, sizeof(tmpbuf));
-    test_streq(tmpbuf, "18.244.0.188");
+    tor_inet_pton(AF_INET, ip, &in);
+    tor_inet_ntop(AF_INET, &in, tmpbuf, sizeof(tmpbuf));
+    test_streq(tmpbuf, ip);
   }
 
   /* Test 'escaped' */
@@ -1027,12 +1028,67 @@
     test_eq_ip6(&a1, &a2);                                              \
   STMT_END
 
+/*XXXX020 make this macro give useful output on failure, and follow the
+ * conventions of the other test macros.  */
+#define test_internal_ip(a,b) STMT_BEGIN             \
+    r = tor_inet_pton(AF_INET6, a, &t1.sa6.sin6_addr); \
+    test_assert(r==1);                               \
+    t1.sa6.sin6_family = AF_INET6;                   \
+    r = tor_addr_is_internal(&t1, b);                \
+    test_assert(r==1);                               \
+  STMT_END
+
+/*XXXX020 make this macro give useful output on failure, and follow the
+ * conventions of the other test macros.  */
+#define test_external_ip(a,b) STMT_BEGIN             \
+    r = tor_inet_pton(AF_INET6, a, &t1.sa6.sin6_addr); \
+    test_assert(r==1);                               \
+    t1.sa6.sin6_family = AF_INET6;                   \
+    r = tor_addr_is_internal(&t1, b);                \
+    test_assert(r==0);                               \
+  STMT_END
+
+/*XXXX020 make this macro give useful output on failure, and follow the
+ * conventions of the other test macros.  */
+#define test_addr_convert6(a,b) STMT_BEGIN           \
+    tor_inet_pton(AF_INET6, a, &t1.sa6.sin6_addr);   \
+    tor_inet_pton(AF_INET6, b, &t2.sa6.sin6_addr);   \
+    t1.sa6.sin6_family = AF_INET6;                   \
+    t2.sa6.sin6_family = AF_INET6;                   \
+  STMT_END
+
+/*XXXX020 make this macro give useful output on failure, and follow the
+ * conventions of the other test macros. */
+#define test_addr_parse(xx) STMT_BEGIN                                \
+    r=tor_addr_parse_mask_ports(xx, &t1, &mask, &port1, &port2);      \
+    t2.sa6.sin6_family = AF_INET6;                                    \
+    p1=tor_inet_ntop(AF_INET6, &t1.sa6.sin6_addr, bug, sizeof(bug));  \
+  STMT_END
+
+/*XXXX020 make this macro give useful output on failure, and follow the
+ * conventions of the other test macros. */
+#define test_addr_parse_check(ip1, ip2, ip3, ip4, mm, pt1, pt2) STMT_BEGIN  \
+    test_assert(r>=0);                                     \
+    test_eq(htonl(ip1), IN6_ADDRESS(&t1)->s6_addr32[0]);   \
+    test_eq(htonl(ip2), IN6_ADDRESS(&t1)->s6_addr32[1]);   \
+    test_eq(htonl(ip3), IN6_ADDRESS(&t1)->s6_addr32[2]);   \
+    test_eq(htonl(ip4), IN6_ADDRESS(&t1)->s6_addr32[3]);   \
+    test_eq(mask, mm);                                     \
+    test_eq(port1, pt1);                                   \
+    test_eq(port2, pt2);                                   \
+  STMT_END
+
 static void
 test_ip6_helpers(void)
 {
-  char buf[64];
+  char buf[TOR_ADDR_BUF_LEN], bug[TOR_ADDR_BUF_LEN];
   struct in6_addr a1, a2;
+  tor_addr_t t1, t2;
   int r, i;
+  uint16_t port1, port2;
+  maskbits_t mask;
+  const char *p1;
+
   //  struct in_addr b1, b2;
   /* Test tor_inet_ntop and tor_inet_pton: IPv6 */
 
@@ -1066,8 +1122,13 @@
   test_ntop6_reduces("0001:0099:BEEF:0006:0123:FFFF:0001:0001",
                      "1:99:beef:6:123:ffff:1:1");
 
-  test_ntop6_reduces("0:0:0:0:0:0:c0a8:0101", "::192.168.1.1");
+  //test_ntop6_reduces("0:0:0:0:0:0:c0a8:0101", "::192.168.1.1");
   test_ntop6_reduces("0:0:0:0:0:ffff:c0a8:0101", "::ffff:192.168.1.1");
+  test_ntop6_reduces("002:0:0000:0:3::4", "2::3:0:0:4");
+  test_ntop6_reduces("0:0::1:0:3", "::1:0:3");
+  test_ntop6_reduces("008:0::0", "8::");
+  test_ntop6_reduces("0:0:0:0:0:ffff::1", "::ffff:0.0.0.1");
+  test_ntop6_reduces("abcd:0:0:0:0:0:7f00::", "abcd::7f00:0");
   test_ntop6_reduces("0000:0000:0000:0000:0009:C0A8:0001:0001",
                      "::9:c0a8:1:1");
   test_ntop6_reduces("fe80:0000:0000:0000:0202:1111:0001:0001",
@@ -1080,7 +1141,7 @@
   test_pton6_bad("55555::");
   test_pton6_bad("9:-60::");
   test_pton6_bad("1:2:33333:4:0002:3::");
-  // test_pton6_bad("1:2:3333:4:00002:3::"); //XXXX not bad.
+  //test_pton6_bad("1:2:3333:4:00002:3::");// BAD, but glibc doesn't say so.
   test_pton6_bad("1:2:3333:4:fish:3::");
   test_pton6_bad("1:2:3:4:5:6:7:8:9");
   test_pton6_bad("1:2:3:4:5:6:7");
@@ -1090,6 +1151,130 @@
   test_pton6_bad("::1.2.3.4.5");
   test_pton6_bad("99");
   test_pton6_bad("");
+  test_pton6_bad("1::2::3:4");
+  test_pton6_bad("a:::b:c");
+  test_pton6_bad(":::a:b:c");
+  test_pton6_bad("a:b:c:::");
+
+  /* test internal checking */
+  test_external_ip("fbff:ffff::2:7", 0);
+  test_internal_ip("fc01::2:7", 0);
+  test_internal_ip("fdff:ffff::f:f", 0);
+  test_external_ip("fe00::3:f", 0);
+
+  test_external_ip("fe7f:ffff::2:7", 0);
+  test_internal_ip("fe80::2:7", 0);
+  test_internal_ip("febf:ffff::f:f", 0);
+
+  test_internal_ip("fec0::2:7:7", 0);
+  test_internal_ip("feff:ffff::e:7:7", 0);
+  test_external_ip("ff00::e:7:7", 0);
+
+  test_internal_ip("::", 0);
+  test_internal_ip("::1", 0);
+  test_internal_ip("::1", 1);
+  test_internal_ip("::", 0);
+  test_external_ip("::", 1);
+  test_external_ip("::2", 0);
+  test_external_ip("2001::", 0);
+  test_external_ip("ffff::", 0);
+
+  test_external_ip("::ffff:0.0.0.0", 1);
+  test_internal_ip("::ffff:0.0.0.0", 0);
+  test_internal_ip("::ffff:0.255.255.255", 0);
+  test_external_ip("::ffff:1.0.0.0", 0);
+
+  test_external_ip("::ffff:9.255.255.255", 0);
+  test_internal_ip("::ffff:10.0.0.0", 0);
+  test_internal_ip("::ffff:10.255.255.255", 0);
+  test_external_ip("::ffff:11.0.0.0", 0);
+
+  test_external_ip("::ffff:126.255.255.255", 0);
+  test_internal_ip("::ffff:127.0.0.0", 0);
+  test_internal_ip("::ffff:127.255.255.255", 0);
+  test_external_ip("::ffff:128.0.0.0", 0);
+
+  test_external_ip("::ffff:172.15.255.255", 0);
+  test_internal_ip("::ffff:172.16.0.0", 0);
+  test_internal_ip("::ffff:172.31.255.255", 0);
+  test_external_ip("::ffff:172.32.0.0", 0);
+
+  test_external_ip("::ffff:192.167.255.255", 0);
+  test_internal_ip("::ffff:192.168.0.0", 0);
+  test_internal_ip("::ffff:192.168.255.255", 0);
+  test_external_ip("::ffff:192.169.0.0", 0);
+
+  test_external_ip("::ffff:169.253.255.255", 0);
+  test_internal_ip("::ffff:169.254.0.0", 0);
+  test_internal_ip("::ffff:169.254.255.255", 0);
+  test_external_ip("::ffff:169.255.0.0", 0);
+
+  /* tor_addr_compare(tor_addr_t x2) */
+  test_addr_convert6("ffff::", "ffff::0");
+  test_assert(tor_addr_compare(&t1, &t2) == 0);
+  test_addr_convert6("0::3:2:1", "0::ffff:0.3.2.1");
+  test_assert(tor_addr_compare(&t1, &t2) > 0);
+  test_addr_convert6("0::2:2:1", "0::ffff:0.3.2.1");
+  test_assert(tor_addr_compare(&t1, &t2) > 0);
+  test_addr_convert6("0::ffff:0.3.2.1", "0::0:0:0");
+  test_assert(tor_addr_compare(&t1, &t2) < 0);
+  test_addr_convert6("0::ffff:5.2.2.1", "::ffff:6.0.0.0");
+  test_assert(tor_addr_compare(&t1, &t2) < 0); /* XXXX wrong. */
+  tor_addr_parse_mask_ports("[::ffff:2.3.4.5]", &t1, NULL, NULL, NULL);
+  tor_addr_parse_mask_ports("2.3.4.5", &t2, NULL, NULL, NULL);
+  test_assert(tor_addr_compare(&t1, &t2) == 0);
+  tor_addr_parse_mask_ports("[::ffff:2.3.4.4]", &t1, NULL, NULL, NULL);
+  tor_addr_parse_mask_ports("2.3.4.5", &t2, NULL, NULL, NULL);
+  test_assert(tor_addr_compare(&t1, &t2) < 0);
+
+  /* XXXX020 test compare_masked */
+
+  /* test tor_addr_parse_mask_ports */
+  test_addr_parse("[::f]/17:47-95");
+  test_addr_parse_check(0, 0, 0, 0x0000000f, 17, 47, 95);
+  //test_addr_parse("[::fefe:4.1.1.7/120]:999-1000");
+  //test_addr_parse_check("::fefe:401:107", 120, 999, 1000);
+  test_addr_parse("[::ffff:4.1.1.7]/120:443");
+  test_addr_parse_check(0, 0, 0x0000ffff, 0x04010107, 120, 443, 443);
+  test_addr_parse("[abcd:2::44a:0]:2-65000");
+  test_addr_parse_check(0xabcd0002, 0, 0, 0x044a0000, 128, 2, 65000);
+
+  r=tor_addr_parse_mask_ports("[fefef::]/112", &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("efef::/112", &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("[f:f:f:f:f:f:f:f::]", &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("[::f:f:f:f:f:f:f:f]", &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("[f:f:f:f:f:f:f:f:f]", &t1, NULL, NULL, NULL);
+  test_assert(r == -1);
+  /* Test for V4-mapped address with mask < 96.  (arguably not valid) */
+  r=tor_addr_parse_mask_ports("[::ffff:1.1.2.2/33]", &t1, &mask, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("1.1.2.2/33", &t1, &mask, NULL, NULL);
+  test_assert(r == -1);
+  r=tor_addr_parse_mask_ports("1.1.2.2/31", &t1, &mask, NULL, NULL);
+  test_assert(r == AF_INET);
+  r=tor_addr_parse_mask_ports("[efef::]/112", &t1, &mask, &port1, &port2);
+  test_assert(r == AF_INET6);
+  test_assert(port1 == 1);
+  test_assert(port2 == 65535);
+
+  /* make sure inet address lengths >= max */
+  test_assert(INET_NTOA_BUF_LEN >= sizeof("255.255.255.255"));
+  test_assert(TOR_ADDR_BUF_LEN >=
+              sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"));
+
+  test_assert(sizeof(tor_addr_t) >= sizeof(struct sockaddr_in6));
+
+  /* get interface addresses */
+  r = get_interface_address6(0, AF_INET, &t1);
+  i = get_interface_address6(0, AF_INET6, &t2);
+  tor_inet_ntop(AF_INET, &t1.sa.sin_addr, buf, sizeof(buf));
+  printf("\nv4 address: %s  (family=%i)", buf, IN_FAMILY(&t1));
+  tor_inet_ntop(AF_INET6, &t2.sa6.sin6_addr, buf, sizeof(buf));
+  printf("\nv6 address: %s  (family=%i)", buf, IN_FAMILY(&t2));
 }
 
 static void
@@ -1098,7 +1283,7 @@
   smartlist_t *sl;
   char *cp;
 
-  /* XXXX test sort_strings, sort_digests, uniq_strings, uniq_digests */
+  /* XXXX test sort_digests, uniq_strings, uniq_digests */
 
   /* Test smartlist add, del_keeporder, insert, get. */
   sl = smartlist_create();
@@ -1359,6 +1544,43 @@
   smartlist_free(sl);
 }
 
+static void
+test_bitarray(void)
+{
+  bitarray_t *ba;
+  int i, j, ok=1;
+
+  ba = bitarray_init_zero(1);
+  test_assert(! bitarray_is_set(ba, 0));
+  bitarray_set(ba, 0);
+  test_assert(bitarray_is_set(ba, 0));
+  bitarray_clear(ba, 0);
+  test_assert(! bitarray_is_set(ba, 0));
+  bitarray_free(ba);
+
+  ba = bitarray_init_zero(1023);
+  for (i = 1; i < 64; ) {
+    for (j = 0; j < 1023; ++j) {
+      if (j % i)
+        bitarray_set(ba, j);
+      else
+        bitarray_clear(ba, j);
+    }
+    for (j = 0; j < 1023; ++j) {
+      if (!bool_eq(bitarray_is_set(ba, j), j%i))
+        ok = 0;
+    }
+    test_assert(ok);
+    if (i < 7)
+      ++i;
+    else if (i == 28)
+      i = 32;
+    else
+      i += 7;
+  }
+  bitarray_free(ba);
+}
+
 /* stop threads running at once. */
 static tor_mutex_t *_thread_test_mutex = NULL;
 /* make sure that threads have to run at the same time. */
@@ -1391,7 +1613,7 @@
   tor_snprintf(buf, sizeof(buf), "%lu", tor_get_thread_id());
   cp = tor_strdup(buf);
 
-  for (i=0; i<100000; ++i) {
+  for (i=0; i<10000; ++i) {
     tor_mutex_acquire(_thread_test_mutex);
     strmap_set(_thread_test_strmap, "last to run", cp);
     ++*count;
@@ -1437,7 +1659,7 @@
     if (strmap_get(_thread_test_strmap, "thread 1") &&
         strmap_get(_thread_test_strmap, "thread 2")) {
       done = 1;
-    } else if (time(NULL) > started + 10) {
+    } else if (time(NULL) > started + 25) {
       timedout = done = 1;
     }
     tor_mutex_release(_thread_test_mutex);
@@ -1910,12 +2132,12 @@
   ex1.policy_type = ADDR_POLICY_ACCEPT;
   ex1.string = NULL;
   ex1.addr = 0;
-  ex1.msk = 0;
+  ex1.maskbits = 0;
   ex1.prt_min = ex1.prt_max = 80;
   ex1.next = &ex2;
   ex2.policy_type = ADDR_POLICY_REJECT;
   ex2.addr = 18 << 24;
-  ex2.msk = 0xFF000000u;
+  ex2.maskbits = 8;
   ex2.prt_min = ex2.prt_max = 24;
   ex2.next = NULL;
   r2.address = tor_strdup("1.1.1.1");
@@ -2487,23 +2709,109 @@
   /* Check signatures.  the first voter hasn't got one.  The second one
    * does: validate it. */
   voter = smartlist_get(con->voters, 0);
-  test_assert(!voter->pending_signature);
+  test_assert(!voter->signature);
   test_assert(!voter->good_signature);
   test_assert(!voter->bad_signature);
 
   voter = smartlist_get(con->voters, 1);
-  test_assert(voter->pending_signature);
+  test_assert(voter->signature);
   test_assert(!voter->good_signature);
   test_assert(!voter->bad_signature);
   test_assert(!networkstatus_check_voter_signature(con,
                                                smartlist_get(con->voters, 1),
                                                cert3));
-  test_assert(!voter->pending_signature);
+  test_assert(voter->signature);
   test_assert(voter->good_signature);
   test_assert(!voter->bad_signature);
 
-  /* XXXX020 frob with detached signatures */
+  {
+    char *consensus_text2, *consensus_text3;
+    networkstatus_vote_t *con2, *con3;
+    char *detached_text1, *addition1, *detached_text2, *addition2;
+    ns_detached_signatures_t *dsig1, *dsig2;
+    size_t sz;
+    /* Compute the other two signed consensuses. */
+    smartlist_shuffle(votes);
+    consensus_text2 = networkstatus_compute_consensus(votes, 3,
+                                                      cert2->identity_key,
+                                                      sign_skey_2);
+    smartlist_shuffle(votes);
+    consensus_text3 = networkstatus_compute_consensus(votes, 3,
+                                                      cert1->identity_key,
+                                                      sign_skey_1);
+    test_assert(consensus_text2);
+    test_assert(consensus_text3);
+    con2 = networkstatus_parse_vote_from_string(consensus_text2, 0);
+    con3 = networkstatus_parse_vote_from_string(consensus_text3, 0);
+    test_assert(con2);
+    test_assert(con3);
 
+    /* All three should have the same digest. */
+    test_memeq(con->networkstatus_digest, con2->networkstatus_digest,
+               DIGEST_LEN);
+    test_memeq(con->networkstatus_digest, con3->networkstatus_digest,
+               DIGEST_LEN);
+
+    /* Extract a detached signature from con3. */
+    detached_text1 = networkstatus_get_detached_signatures(con3);
+    tor_assert(detached_text1);
+    /* Try to parse it. */
+    dsig1 = networkstatus_parse_detached_signatures(detached_text1, NULL);
+    tor_assert(dsig1);
+
+    /* Are parsed values as expected? */
+    test_eq(dsig1->valid_after, con3->valid_after);
+    test_eq(dsig1->fresh_until, con3->fresh_until);
+    test_eq(dsig1->valid_until, con3->valid_until);
+    test_memeq(dsig1->networkstatus_digest, con3->networkstatus_digest,
+               DIGEST_LEN);
+    test_eq(1, smartlist_len(dsig1->signatures));
+    voter = smartlist_get(dsig1->signatures, 0);
+    test_memeq(voter->identity_digest, cert1->cache_info.identity_digest,
+               DIGEST_LEN);
+
+    /* Try adding it to con2. */
+    detached_text2 = networkstatus_get_detached_signatures(con2);
+    addition1 = NULL;
+    test_eq(1, networkstatus_add_detached_signatures(con2, dsig1, &addition1));
+    sz = strlen(detached_text2)+strlen(addition1)+1;
+    detached_text2 = tor_realloc(detached_text2, sz);
+    strlcat(detached_text2, addition1, sz);
+    //printf("\n<%s>\n", detached_text2);
+    dsig2 = networkstatus_parse_detached_signatures(detached_text2, NULL);
+    test_assert(dsig2);
+    /*
+    printf("\n");
+    SMARTLIST_FOREACH(dsig2->signatures, networkstatus_voter_info_t *, vi, {
+        char hd[64];
+        base16_encode(hd, sizeof(hd), vi->identity_digest, DIGEST_LEN);
+        printf("%s\n", hd);
+      });
+    */
+    test_eq(2, smartlist_len(dsig2->signatures));
+
+    /* Add to con. */
+    test_eq(2, networkstatus_add_detached_signatures(con, dsig2, &addition2));
+    /* Check signatures */
+    test_assert(!networkstatus_check_voter_signature(con,
+                                               smartlist_get(con->voters, 0),
+                                               cert2));
+    test_assert(!networkstatus_check_voter_signature(con,
+                                               smartlist_get(con->voters, 2),
+                                               cert1));
+
+    networkstatus_vote_free(con2);
+    networkstatus_vote_free(con3);
+    tor_free(consensus_text2);
+    tor_free(consensus_text3);
+    tor_free(detached_text1);
+    tor_free(detached_text2);
+    ns_detached_signatures_free(dsig1);
+    ns_detached_signatures_free(dsig2);
+    tor_free(addition1);
+    tor_free(addition2);
+  }
+
   smartlist_free(votes);
   tor_free(v1_text);
   tor_free(v2_text);
@@ -2526,13 +2834,15 @@
 test_policies(void)
 {
   addr_policy_t *policy, *policy2;
+  tor_addr_t tar;
   config_line_t line;
 
   policy = router_parse_addr_policy_from_string("reject 192.168.0.0/16:*",-1);
   test_eq(NULL, policy->next);
   test_eq(ADDR_POLICY_REJECT, policy->policy_type);
-  test_eq(0xc0a80000u, policy->addr);
-  test_eq(0xffff0000u, policy->msk);
+  tor_addr_from_ipv4(&tar, 0xc0a80000u);
+  test_assert(policy->addr == 0xc0a80000u);
+  test_eq(16, policy->maskbits);
   test_eq(1, policy->prt_min);
   test_eq(65535, policy->prt_max);
   test_streq("reject 192.168.0.0/16:*", policy->string);
@@ -2589,7 +2899,7 @@
   pk1 = pk_generate(0);
   pk2 = pk_generate(1);
 
-  /* Test unversioned descriptor */
+  /* Test v0 descriptor */
   d1 = tor_malloc_zero(sizeof(rend_service_descriptor_t));
   d1->pk = crypto_pk_dup_key(pk1);
   now = time(NULL);
@@ -2600,7 +2910,7 @@
   d1->intro_points[0] = tor_strdup("tom");
   d1->intro_points[1] = tor_strdup("crow");
   d1->intro_points[2] = tor_strdup("joel");
-  test_assert(! rend_encode_service_descriptor(d1, 0, pk1, &encoded, &len));
+  test_assert(! rend_encode_service_descriptor(d1, pk1, &encoded, &len));
   d2 = rend_parse_service_descriptor(encoded, len);
   test_assert(d2);
 
@@ -2618,8 +2928,9 @@
   rend_service_descriptor_free(d2);
   tor_free(encoded);
 
-  /* Test versioned descriptor. */
-  d1 = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  /* Test v2 descriptor. */
+    
+  /*d1 = tor_malloc_zero(sizeof(rend_service_descriptor_t));
   d1->pk = crypto_pk_dup_key(pk1);
   now = time(NULL);
   d1->timestamp = now;
@@ -2642,38 +2953,38 @@
   d1->intro_point_extend_info[1]->addr = 6060842;
   d1->intro_point_extend_info[1]->port = 8000;
   d1->intro_point_extend_info[1]->onion_key = crypto_pk_dup_key(pk2);
-  memset(d1->intro_point_extend_info[1]->identity_digest, 'b', DIGEST_LEN);
+  memset(d1->intro_point_extend_info[1]->identity_digest, 'b', DIGEST_LEN);*/
 
-  test_assert(! rend_encode_service_descriptor(d1, 1, pk1, &encoded, &len));
-  d2 = rend_parse_service_descriptor(encoded, len);
-  test_assert(d2);
+  //test_assert(! rend_encode_service_descriptor(d1, pk1, &encoded, &len));
+  //d2 = rend_parse_service_descriptor(encoded, len);
+  //test_assert(d2);
 
-  test_assert(!crypto_pk_cmp_keys(d1->pk, d2->pk));
-  test_eq(d2->timestamp, now);
-  test_eq(d2->version, 1);
-  test_eq(d2->protocols, 60);
-  test_eq(d2->n_intro_points, 2);
-  test_streq(d2->intro_points[0], d2->intro_point_extend_info[0]->nickname);
-  test_streq(d2->intro_points[1], d2->intro_point_extend_info[1]->nickname);
-  test_eq(d2->intro_point_extend_info[0]->addr, 1234);
-  test_eq(d2->intro_point_extend_info[0]->port, 4567);
-  test_assert(!crypto_pk_cmp_keys(pk1,
-                                  d2->intro_point_extend_info[0]->onion_key));
-  test_memeq(d2->intro_point_extend_info[0]->identity_digest,
-             d1->intro_point_extend_info[0]->identity_digest, DIGEST_LEN);
-  test_eq(d2->intro_point_extend_info[1]->addr, 6060842);
-  test_eq(d2->intro_point_extend_info[1]->port, 8000);
+  //test_assert(!crypto_pk_cmp_keys(d1->pk, d2->pk));
+  //test_eq(d2->timestamp, now);
+  //test_eq(d2->version, 1);
+  //test_eq(d2->protocols, 60);
+  //test_eq(d2->n_intro_points, 2);
+  //test_streq(d2->intro_points[0], d2->intro_point_extend_info[0]->nickname);
+  //test_streq(d2->intro_points[1], d2->intro_point_extend_info[1]->nickname);
+  //test_eq(d2->intro_point_extend_info[0]->addr, 1234);
+  //test_eq(d2->intro_point_extend_info[0]->port, 4567);
+  //test_assert(!crypto_pk_cmp_keys(pk1,
+  //                                d2->intro_point_extend_info[0]->onion_key));
+  //test_memeq(d2->intro_point_extend_info[0]->identity_digest,
+  //           d1->intro_point_extend_info[0]->identity_digest, DIGEST_LEN);
+  //test_eq(d2->intro_point_extend_info[1]->addr, 6060842);
+  //test_eq(d2->intro_point_extend_info[1]->port, 8000);
 
-  test_memeq(d2->intro_point_extend_info[1]->identity_digest,
-             d1->intro_point_extend_info[1]->identity_digest, DIGEST_LEN);
+  //test_memeq(d2->intro_point_extend_info[1]->identity_digest,
+  //           d1->intro_point_extend_info[1]->identity_digest, DIGEST_LEN);
 
   test_assert(BAD_HOSTNAME == parse_extended_hostname(address1));
   test_assert(ONION_HOSTNAME == parse_extended_hostname(address2));
   test_assert(EXIT_HOSTNAME == parse_extended_hostname(address3));
   test_assert(NORMAL_HOSTNAME == parse_extended_hostname(address4));
 
-  rend_service_descriptor_free(d1);
-  rend_service_descriptor_free(d2);
+  //rend_service_descriptor_free(d1);
+  //rend_service_descriptor_free(d2);
   crypto_free_pk_env(pk1);
   crypto_free_pk_env(pk2);
 }
@@ -2757,6 +3068,157 @@
   smartlist_free(allocated);
 }
 
+/* Test AES-CBC encryption and decryption. */
+static void
+test_aes_cbc(void)
+{
+  char *plain, *encrypted1, *encrypted2, *decrypted1, *decrypted2;
+  char key1[16], key2[16];
+  size_t encrypted_size, decrypted_size;
+  plain = tor_malloc(4095);
+  encrypted1 = tor_malloc(4095 + 1 + 16);
+  encrypted2 = tor_malloc(4095 + 1 + 16);
+  decrypted1 = tor_malloc(4095 + 1);
+  decrypted2 = tor_malloc(4095 + 1);
+  crypto_rand(plain, 4095);
+  crypto_rand(key1, 16);
+  crypto_rand(key2, 16);
+  key1[0] = key2[0] + 128; /* Make sure that contents are different. */
+  /* Encrypt and decrypt with the same key. */
+  encrypted_size = crypto_cipher_encrypt_cbc(key1, encrypted1, plain, 4095);
+  test_eq(encrypted_size, 4095 + 1 + 16);
+  decrypted_size = crypto_cipher_decrypt_cbc(key1, decrypted1, encrypted1,
+                                             encrypted_size);
+  test_eq(decrypted_size, 4095);
+  test_memeq(plain, decrypted1, 4095);
+  /* Encrypt a second time (with a new random initialization vector). */
+  encrypted_size = crypto_cipher_encrypt_cbc(key1, encrypted2, plain, 4095);
+  test_eq(encrypted_size, 4095 + 1 + 16);
+  decrypted_size = crypto_cipher_decrypt_cbc(key1, decrypted2, encrypted2,
+                                             encrypted_size);
+  test_eq(decrypted_size, 4095);
+  test_memeq(plain, decrypted2, 4095);
+  test_memneq(encrypted1, encrypted2, encrypted_size);
+  /* Decrypt with the wrong key. */
+  decrypted_size = crypto_cipher_decrypt_cbc(key2, decrypted2, encrypted1,
+                                             encrypted_size);
+  test_eq(decrypted_size, -1);
+  /* Alter the initialization vector. */
+  encrypted1[0] += 42;
+  decrypted_size = crypto_cipher_decrypt_cbc(key1, decrypted1, encrypted1,
+                                             encrypted_size);
+  test_memneq(plain, decrypted2, 4095);
+  /* Free memory. */
+  tor_free(plain);
+  tor_free(encrypted1);
+  tor_free(encrypted2);
+  tor_free(decrypted1);
+  tor_free(decrypted2);
+}
+
+/* Test base32 decoding. */
+static void
+test_base32_decode(void)
+{
+  char plain[60], encoded[96 + 1], decoded[60];
+  int res;
+  crypto_rand(plain, 60);
+  /* Encode and decode a random string. */
+  base32_encode(encoded, 96 + 1, plain, 60);
+  res = base32_decode(decoded, 60, encoded, 96);
+  test_eq(res, 0);
+  test_memeq(plain, decoded, 60);
+  /* Change encoded string and decode. */
+  if (encoded[0] == 'a')
+    encoded[0] = 'b';
+  else
+    encoded[0] = 'a';
+  res = base32_decode(decoded, 60, encoded, 96);
+  test_eq(res, 0);
+  test_memneq(plain, decoded, 60);
+}
+
+/* Test encoding and parsing of v2 rendezvous service descriptors. */
+static void
+test_v2_rend_desc(void)
+{
+  rend_service_descriptor_t *generated, *parsed;
+  smartlist_t *desc_strs = smartlist_create();
+  char desc_ids[NUMBER_OF_NON_CONSECUTIVE_REPLICAS][DIGEST_LEN];
+  char parsed_desc_id[DIGEST_LEN];
+  crypto_pk_env_t *pk1;
+  time_t now;
+  char secret_cookie_bin[15];
+  char secret_cookie_base32[24 + 1];
+  char *intro_points_encrypted;
+  size_t intro_points_size;
+  int i;
+  pk1 = pk_generate(0);
+  crypto_rand(secret_cookie_bin, 15);
+  base32_encode(secret_cookie_base32, 24 + 1, secret_cookie_bin, 15);
+  generated = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  generated->pk = crypto_pk_dup_key(pk1);
+  now = time(NULL);
+  generated->timestamp = now;
+  generated->n_intro_points = 3;
+  generated->version = 2;
+  generated->protocols = 42;
+  generated->intro_point_extend_info =
+    tor_malloc_zero(sizeof(extend_info_t *) * generated->n_intro_points);
+  generated->intro_points =
+    tor_malloc_zero(sizeof(char *) * generated->n_intro_points);
+  for (i = 0; i < generated->n_intro_points; i++) {
+    extend_info_t *info = tor_malloc_zero(sizeof(extend_info_t));
+    crypto_pk_env_t *okey = pk_generate(2 + i);
+    info->onion_key = crypto_pk_dup_key(okey);
+    crypto_pk_get_digest(info->onion_key, info->identity_digest);
+    //crypto_rand(info->identity_digest, DIGEST_LEN); /* Would this work? */
+    info->nickname[0] = '$';
+    base16_encode(info->nickname + 1, sizeof(info->nickname) - 1,
+                  info->identity_digest, DIGEST_LEN);
+    info->addr = crypto_rand_int(65536); /* Does not cover all IP addresses. */
+    info->port = crypto_rand_int(65536);
+    /* TODO114 Add service key. */
+    generated->intro_points[i] = tor_strdup(info->nickname);
+    generated->intro_point_extend_info[i] = info;
+  }
+  test_assert(rend_encode_v2_descriptors(desc_strs, desc_ids, generated, now,
+                                         secret_cookie_base32, 0) > 0);
+  test_assert(rend_parse_v2_service_descriptor(&parsed, parsed_desc_id,
+                                               &intro_points_encrypted,
+                                               &intro_points_size,
+                                               (const char **)&desc_strs->list[0],
+                                               0) == 0);
+  test_assert(parsed);
+  test_assert(rend_decrypt_introduction_points(parsed, secret_cookie_base32,
+                                               intro_points_encrypted,
+                                               intro_points_size) == 3);
+  test_assert(!crypto_pk_cmp_keys(generated->pk, parsed->pk));
+  test_eq(parsed->timestamp, now);
+  test_eq(parsed->version, 2);
+  test_eq(parsed->protocols, 42);
+  test_eq(parsed->n_intro_points, 3);
+  for (i = 0; i < parsed->n_intro_points; i++) {
+    extend_info_t *par_info = parsed->intro_point_extend_info[i];
+    extend_info_t *gen_info = generated->intro_point_extend_info[i];
+    test_assert(!crypto_pk_cmp_keys(gen_info->onion_key, par_info->onion_key));
+    test_memeq(gen_info->identity_digest, par_info->identity_digest,
+               DIGEST_LEN);
+    test_streq(gen_info->nickname, par_info->nickname);
+    test_streq(generated->intro_points[i], parsed->intro_points[i]);
+    test_eq(gen_info->addr, par_info->addr);
+    test_eq(gen_info->port, par_info->port);
+  }
+  tor_free(intro_points_encrypted);
+  /*for (i = 0; i < NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++)
+    tor_free(desc_strs[i]);*/
+  smartlist_free(desc_strs);
+  for (i = 0; i < parsed->n_intro_points; i++) {
+    tor_free(parsed->intro_point_extend_info[i]->onion_key);
+    tor_free(generated->intro_point_extend_info[i]->onion_key);
+  }
+}
+
 int
 main(int c, char**v)
 {
@@ -2803,6 +3265,8 @@
   test_util();
   puts("\n--smartlist");
   test_smartlist();
+  puts("\n--bitarray");
+  test_bitarray();
   puts("\n--mempool");
   test_mempool();
   puts("\n--strmap");
@@ -2824,6 +3288,10 @@
   test_policies();
   puts("\n========================= Rendezvous functionality ========");
   test_rend_fns();
+  puts("\n========================= Rendezvous v2 functionality =====");
+  test_aes_cbc();
+  test_base32_decode();
+  test_v2_rend_desc();
   puts("");
 
   if (have_failed)



More information about the tor-commits mailing list