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

[or-cvs] r16566: {tor} Merge with trunk once more. (in tor/branches/121-hs-authorization: doc src/common src/or)



Author: kloesing
Date: 2008-08-16 07:36:09 -0400 (Sat, 16 Aug 2008)
New Revision: 16566

Modified:
   tor/branches/121-hs-authorization/doc/tor.1.in
   tor/branches/121-hs-authorization/src/common/address.c
   tor/branches/121-hs-authorization/src/common/address.h
   tor/branches/121-hs-authorization/src/common/crypto.c
   tor/branches/121-hs-authorization/src/common/crypto.h
   tor/branches/121-hs-authorization/src/common/mempool.c
   tor/branches/121-hs-authorization/src/or/buffers.c
   tor/branches/121-hs-authorization/src/or/config.c
   tor/branches/121-hs-authorization/src/or/connection.c
   tor/branches/121-hs-authorization/src/or/connection_edge.c
   tor/branches/121-hs-authorization/src/or/connection_or.c
   tor/branches/121-hs-authorization/src/or/control.c
   tor/branches/121-hs-authorization/src/or/cpuworker.c
   tor/branches/121-hs-authorization/src/or/directory.c
   tor/branches/121-hs-authorization/src/or/dirserv.c
   tor/branches/121-hs-authorization/src/or/dirvote.c
   tor/branches/121-hs-authorization/src/or/dnsserv.c
   tor/branches/121-hs-authorization/src/or/main.c
   tor/branches/121-hs-authorization/src/or/networkstatus.c
   tor/branches/121-hs-authorization/src/or/or.h
   tor/branches/121-hs-authorization/src/or/policies.c
   tor/branches/121-hs-authorization/src/or/rendclient.c
   tor/branches/121-hs-authorization/src/or/rendservice.c
   tor/branches/121-hs-authorization/src/or/routerlist.c
   tor/branches/121-hs-authorization/src/or/routerparse.c
   tor/branches/121-hs-authorization/src/or/test.c
Log:
Merge with trunk once more.

Modified: tor/branches/121-hs-authorization/doc/tor.1.in
===================================================================
--- tor/branches/121-hs-authorization/doc/tor.1.in	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/doc/tor.1.in	2008-08-16 11:36:09 UTC (rev 16566)
@@ -422,10 +422,18 @@
 .LP
 .TP
 \fBExcludeNodes \fR\fInode\fR,\fInode\fR,\fI...\fP
-A list of identity fingerprints or nicknames of nodes to never use when
-building a circuit.
+A list of identity fingerprints, nicknames, and address patterns of
+nodes to never use when building a circuit.  (Example: ExcludeNodes
+SlowServer, $ABCDEFFFFFFFFFFFFFFF, 255.254.0.0/8)
 .LP
 .TP
+\fBExcludeExitNodes \fR\fInode\fR,\fInode\fR,\fI...\fP
+A list of identity fingerprints, nicknames, and address patterns of
+nodes to never use when picking an exit node.  Note that any node
+listed in ExcludeNodes is automatically considered to be part of this
+list.
+.LP
+.TP
 \fBEntryNodes \fR\fInode\fR,\fInode\fR,\fI...\fP
 A list of identity fingerprints or nicknames of preferred nodes to use for the
 first hop in the circuit.
@@ -552,16 +560,6 @@
 .\" .TP
 .LP
 .TP
-\fBRendNodes \fR\fInode\fR,\fInode\fR,\fI...\fP
-A list of identity fingerprints or nicknames of preferred nodes to use for the
-rendezvous point, if possible.
-.LP
-.TP
-\fBRendExcludeNodes \fR\fInode\fR,\fInode\fR,\fI...\fP
-A list of identity fingerprints or nicknames of nodes to never use when
-choosing a rendezvous point.
-.LP
-.TP
 \fBSocksPort \fR\fIPORT\fP
 Advertise this port to listen for connections from Socks-speaking
 applications.  Set this to 0 if you don't want to allow application
@@ -588,12 +586,6 @@
 (Default: 2 minutes.)
 .LP
 .TP
-\fBTestVia \fR\fInode\fR,\fInode\fR,\fI...\fP
-A list of identity fingerprints or nicknames of nodes to prefer for your middle
-hop when building testing circuits. This option is mainly for debugging
-reachability problems.
-.LP
-.TP
 \fBTrackHostExits \fR\fIhost\fR,\fI.domain\fR,\fI...\fR\fP
 For each value in the comma separated list, Tor will track recent connections
 to hosts that match this value and attempt to
@@ -724,7 +716,7 @@
 address that ends with one of the suffixes in
 \fBAutomapHostsSuffixes\fP, we map an unused virtual address to that
 address, and return the new virtual address.  This is handy for making
-.onion addresses work with applications that resolve an address and
+".onion" addresses work with applications that resolve an address and
 then connect to it.
 (Default: 0).
 .LP
@@ -1274,19 +1266,6 @@
 one of the TARGETs from those lines will be chosen at random.
 .LP
 .TP
-\fBHiddenServiceNodes \fR\fInode\fR,\fInode\fR,\fI...\fP
-If possible, use the specified nodes, defined by their identity fingerprints or
-nicknames, as introduction points for the hidden service. If this is left
-unset, Tor will be smart and pick some reasonable ones; most people can leave
-this unset.
-.LP
-.TP
-\fBHiddenServiceExcludeNodes \fR\fInode\fR,\fInode\fR,\fI...\fP
-Do not use the specified nodes, defined by their identity fingerprints or
-nicknames, as introduction points for the hidden service. In normal use there
-is no reason to set this.
-.LP
-.TP
 \fBPublishHidServDescriptors \fR\fB0\fR|\fB1\fR\fP
 If set to 0, Tor will run any hidden services you configure, but it won't
 advertise them to the rendezvous directory. This option is only useful

Modified: tor/branches/121-hs-authorization/src/common/address.c
===================================================================
--- tor/branches/121-hs-authorization/src/common/address.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/common/address.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -94,7 +94,8 @@
 /** Set the tor_addr_t in <b>a</b> to contain the socket address contained in
  * <b>sa</b>. */
 int
-tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa)
+tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa,
+                       uint16_t *port_out)
 {
   tor_assert(a);
   tor_assert(sa);
@@ -103,10 +104,14 @@
     struct sockaddr_in *sin = (struct sockaddr_in *) sa;
     a->family = AF_INET;
     a->addr.in_addr.s_addr = sin->sin_addr.s_addr;
+    if (port_out)
+      *port_out = ntohs(sin->sin_port);
   } else if (sa->sa_family == AF_INET6) {
     struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
     a->family = AF_INET6;
     memcpy(&a->addr.in6_addr, &sin6->sin6_addr, sizeof(struct in6_addr));
+    if (port_out)
+      *port_out = ntohs(sin6->sin6_port);
   } else {
     a->family = AF_UNSPEC;
     return -1;
@@ -630,6 +635,13 @@
   memcpy(dest->addr.in6_addr.s6_addr, ipv6_bytes, 16);
 }
 
+/** DOCDOC */
+void
+tor_addr_from_in6(tor_addr_t *dest, const struct in6_addr *in6)
+{
+  tor_addr_from_ipv6_bytes(dest, (const char*)in6->s6_addr);
+}
+
 /** Copy a tor_addr_t from <b>src</b> to <b>dest</b>.
  */
 void

Modified: tor/branches/121-hs-authorization/src/common/address.h
===================================================================
--- tor/branches/121-hs-authorization/src/common/address.h	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/common/address.h	2008-08-16 11:36:09 UTC (rev 16566)
@@ -40,7 +40,8 @@
 static INLINE int tor_addr_eq_ipv4h(const tor_addr_t *a, uint32_t u);
 socklen_t tor_addr_to_sockaddr(const tor_addr_t *a, uint16_t port,
                                struct sockaddr *sa_out, socklen_t len);
-int tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa);
+int tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa,
+                           uint16_t *port_out);
 void tor_addr_make_unspec(tor_addr_t *a);
 
 static INLINE const struct in6_addr *
@@ -126,7 +127,8 @@
  * order. */
 #define tor_addr_from_ipv4h(dest, v4addr)       \
   tor_addr_from_ipv4n((dest), htonl(v4addr))
-void tor_addr_from_ipv6_bytes(tor_addr_t *dest, const char *byets);
+void tor_addr_from_ipv6_bytes(tor_addr_t *dest, const char *bytes);
+void tor_addr_from_in6(tor_addr_t *dest, const struct in6_addr *in6);
 int tor_addr_is_null(const tor_addr_t *addr);
 int tor_addr_is_loopback(const tor_addr_t *addr);
 

Modified: tor/branches/121-hs-authorization/src/common/crypto.c
===================================================================
--- tor/branches/121-hs-authorization/src/common/crypto.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/common/crypto.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -474,17 +474,14 @@
   return 0;
 }
 
-/** PEM-encode the public key portion of <b>env</b> and write it to a
- * newly allocated string.  On success, set *<b>dest</b> to the new
- * string, *<b>len</b> to the string's length, and return 0.  On
- * failure, return -1.
- */
-int
-crypto_pk_write_public_key_to_string(crypto_pk_env_t *env, char **dest,
-                                     size_t *len)
+/** Helper function to implement crypto_pk_write_*_key_to_string. */
+static int
+crypto_pk_write_key_to_string_impl(crypto_pk_env_t *env, char **dest,
+                                   size_t *len, int is_public)
 {
   BUF_MEM *buf;
   BIO *b;
+  int r;
 
   tor_assert(env);
   tor_assert(env->key);
@@ -495,8 +492,13 @@
   /* Now you can treat b as if it were a file.  Just use the
    * PEM_*_bio_* functions instead of the non-bio variants.
    */
-  if (!PEM_write_bio_RSAPublicKey(b, env->key)) {
-    crypto_log_errors(LOG_WARN, "writing public key to string");
+  if (is_public)
+    r = PEM_write_bio_RSAPublicKey(b, env->key);
+  else
+    r = PEM_write_bio_RSAPrivateKey(b, env->key, NULL,NULL,0,NULL,NULL);
+
+  if (!r) {
+    crypto_log_errors(LOG_WARN, "writing RSA key to string");
     BIO_free(b);
     return -1;
   }
@@ -515,6 +517,18 @@
   return 0;
 }
 
+/** PEM-encode the public key portion of <b>env</b> and write it to a
+ * newly allocated string.  On success, set *<b>dest</b> to the new
+ * string, *<b>len</b> to the string's length, and return 0.  On
+ * failure, return -1.
+ */
+int
+crypto_pk_write_public_key_to_string(crypto_pk_env_t *env, char **dest,
+                                     size_t *len)
+{
+  return crypto_pk_write_key_to_string_impl(env, dest, len, 1);
+}
+
 /** PEM-encode the private key portion of <b>env</b> and write it to a
  * newly allocated string.  On success, set *<b>dest</b> to the new
  * string, *<b>len</b> to the string's length, and return 0.  On
@@ -524,36 +538,7 @@
 crypto_pk_write_private_key_to_string(crypto_pk_env_t *env, char **dest,
                                      size_t *len)
 {
-  BUF_MEM *buf;
-  BIO *b;
-
-  tor_assert(env);
-  tor_assert(env->key);
-  tor_assert(dest);
-
-  b = BIO_new(BIO_s_mem()); /* Create a memory BIO */
-
-  /* Now you can treat b as if it were a file.  Just use the
-   * PEM_*_bio_* functions instead of the non-bio variants.
-   */
-  if (!PEM_write_bio_RSAPrivateKey(b, env->key, NULL,NULL,0,NULL,NULL)) {
-    crypto_log_errors(LOG_WARN, "writing private key to string");
-    BIO_free(b);
-    return -1;
-  }
-
-  BIO_get_mem_ptr(b, &buf);
-  (void)BIO_set_close(b, BIO_NOCLOSE); /* so BIO_free doesn't free buf */
-  BIO_free(b);
-
-  tor_assert(buf->length >= 0);
-  *dest = tor_malloc(buf->length+1);
-  memcpy(*dest, buf->data, buf->length);
-  (*dest)[buf->length] = 0; /* nul terminate it */
-  *len = buf->length;
-  BUF_MEM_free(buf);
-
-  return 0;
+  return crypto_pk_write_key_to_string_impl(env, dest, len, 0);
 }
 
 /** Read a PEM-encoded public key from the first <b>len</b> characters of
@@ -634,6 +619,15 @@
   return r;
 }
 
+/** Return true iff <b>key</b> contains the private-key portion of the RSA
+ * key. */
+int
+crypto_pk_key_is_private(const crypto_pk_env_t *key)
+{
+  tor_assert(key);
+  return PRIVATE_KEY_OK(key);
+}
+
 /** Compare the public-key components of a and b.  Return -1 if a\<b, 0
  * if a==b, and 1 if a\>b.
  */

Modified: tor/branches/121-hs-authorization/src/common/crypto.h
===================================================================
--- tor/branches/121-hs-authorization/src/common/crypto.h	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/common/crypto.h	2008-08-16 11:36:09 UTC (rev 16566)
@@ -92,6 +92,7 @@
 int crypto_pk_cmp_keys(crypto_pk_env_t *a, crypto_pk_env_t *b);
 size_t crypto_pk_keysize(crypto_pk_env_t *env);
 crypto_pk_env_t *crypto_pk_dup_key(crypto_pk_env_t *orig);
+int crypto_pk_key_is_private(const crypto_pk_env_t *key);
 
 int crypto_pk_public_encrypt(crypto_pk_env_t *env, char *to,
                              const char *from, size_t fromlen, int padding);

Modified: tor/branches/121-hs-authorization/src/common/mempool.c
===================================================================
--- tor/branches/121-hs-authorization/src/common/mempool.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/common/mempool.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -144,7 +144,7 @@
 };
 
 /** Number of extra bytes needed beyond mem_size to allocate a chunk. */
-#define CHUNK_OVERHEAD (sizeof(mp_chunk_t)-1)
+#define CHUNK_OVERHEAD STRUCT_OFFSET(mp_chunk_t, mem[0])
 
 /** Given a pointer to a mp_allocated_t, return a pointer to the memory
  * item it holds. */

Modified: tor/branches/121-hs-authorization/src/or/buffers.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/buffers.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/buffers.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -1309,7 +1309,8 @@
                      int log_sockstype, int safe_socks)
 {
   unsigned int len;
-  char tmpbuf[INET_NTOA_BUF_LEN];
+  char tmpbuf[TOR_ADDR_BUF_LEN+1];
+  tor_addr_t destaddr;
   uint32_t destip;
   uint8_t socksver;
   enum {socks4, socks4a} socks4_prot = socks4a;
@@ -1374,13 +1375,20 @@
       }
       switch (*(buf->head->data+3)) { /* address type */
         case 1: /* IPv4 address */
+        case 4: /* IPv6 address */ {
+          const int is_v6 = *(buf->head->data+3) == 4;
+          const unsigned addrlen = is_v6 ? 16 : 4;
           log_debug(LD_APP,"socks5: ipv4 address type");
-          if (buf->datalen < 10) /* ip/port there? */
+          if (buf->datalen < 6+addrlen) /* ip/port there? */
             return 0; /* not yet */
 
-          destip = ntohl(*(uint32_t*)(buf->head->data+4));
-          in.s_addr = htonl(destip);
-          tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
+          if (is_v6)
+            tor_addr_from_ipv6_bytes(&destaddr, buf->head->data+4);
+          else
+            tor_addr_from_ipv4n(&destaddr, get_uint32(buf->head->data+4));
+
+          tor_addr_to_str(tmpbuf, &destaddr, sizeof(tmpbuf), 1);
+
           if (strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN) {
             log_warn(LD_APP,
                      "socks5 IP takes %d bytes, which doesn't fit in %d. "
@@ -1389,8 +1397,8 @@
             return -1;
           }
           strlcpy(req->address,tmpbuf,sizeof(req->address));
-          req->port = ntohs(*(uint16_t*)(buf->head->data+8));
-          buf_remove_from_front(buf, 10);
+          req->port = ntohs(get_uint16(buf->head->data+4+addrlen));
+          buf_remove_from_front(buf, 6+addrlen);
           if (req->command != SOCKS_COMMAND_RESOLVE_PTR &&
               !addressmap_have_mapping(req->address) &&
               !have_warned_about_unsafe_socks) {
@@ -1410,6 +1418,7 @@
               return -1;
           }
           return 1;
+        }
         case 3: /* fqdn */
           log_debug(LD_APP,"socks5: fqdn address type");
           if (req->command == SOCKS_COMMAND_RESOLVE_PTR) {

Modified: tor/branches/121-hs-authorization/src/or/config.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/config.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/config.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -227,7 +227,7 @@
   VAR("HiddenServicePort",   LINELIST_S, RendConfigLines,    NULL),
   VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines,    NULL),
   VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL),
-  VAR("HidServAuth",             LINELIST_S, ClientSideHidServs, NULL),
+  V(HidServAuth,                 LINELIST, NULL),
   V(HSAuthoritativeDir,          BOOL,     "0"),
   V(HSAuthorityRecordStats,      BOOL,     "0"),
   V(HttpProxy,                   STRING,   NULL),
@@ -1061,6 +1061,16 @@
     }
   }
 
+#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H)
+  /* Open /dev/pf before dropping privileges. */
+  if (options->TransPort) {
+    if (get_pf_socket() < 0) {
+      *msg = tor_strdup("Unable to open /dev/pf for transparent proxy.");
+      goto rollback;
+    }
+  }
+#endif
+
   /* Setuid/setgid as appropriate */
   if (options->User || options->Group) {
     /* XXXX021 We should only do this the first time through, not on
@@ -1182,6 +1192,12 @@
     return -1;
   }
 
+  if (running_tor && rend_parse_service_authorization(options, 0) < 0) {
+    log_warn(LD_BUG, "Previously validated client authorization for "
+                     "hidden services could not be added!");
+    return -1;
+  }
+
   if (running_tor && directory_caches_v2_dir_info(options)) {
     len = strlen(options->DataDirectory)+32;
     fn = tor_malloc(len);
@@ -3145,16 +3161,6 @@
     options->MinUptimeHidServDirectoryV2 = 0;
   }
 
-  /* Parse client-side authorization for hidden services. */
-  if (options->ClientSideHidServs) {
-    for (cl = options->ClientSideHidServs; cl; cl = cl->next) {
-      if (!rend_parse_client_auth(cl->value)) {
-        log_warn(LD_CONFIG, "HidServAuth contains illegal value: '%s'. "
-                            "Discarding.", cl->value);
-      }
-    }
-  }
-
   if (options->RendPostPeriod < MIN_REND_POST_PERIOD) {
     log(LOG_WARN,LD_CONFIG,"RendPostPeriod option must be at least %d seconds."
         " Clipping.", MIN_REND_POST_PERIOD);
@@ -3403,6 +3409,11 @@
   if (rend_config_services(options, 1) < 0)
     REJECT("Failed to configure rendezvous options. See logs for details.");
 
+  /* Parse client-side authorization for hidden services. */
+  if (rend_parse_service_authorization(options, 1) < 0)
+    REJECT("Failed to configure client authorization for hidden services. "
+           "See logs for details.");
+
   if (parse_virtual_addr_network(options->VirtualAddrNetwork, 1, NULL)<0)
     return -1;
 

Modified: tor/branches/121-hs-authorization/src/or/connection.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/connection.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/connection.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -166,7 +166,8 @@
 connection_t *
 connection_new(int type, int socket_family)
 {
-  static uint32_t n_connections_allocated = 1;
+  static uint64_t n_connections_allocated = 1;
+
   connection_t *conn;
   time_t now = time(NULL);
   size_t length;
@@ -200,6 +201,7 @@
   conn->magic = magic;
   conn->s = -1; /* give it a default of 'not used' */
   conn->conn_array_index = -1; /* also default to 'not used' */
+  conn->global_identifier = n_connections_allocated++;
 
   conn->type = type;
   conn->socket_family = socket_family;
@@ -211,9 +213,6 @@
     TO_EDGE_CONN(conn)->socks_request =
       tor_malloc_zero(sizeof(socks_request_t));
   }
-  if (CONN_IS_EDGE(conn)) {
-    TO_EDGE_CONN(conn)->global_identifier = n_connections_allocated++;
-  }
   if (type == CONN_TYPE_OR) {
     TO_OR_CONN(conn)->timestamp_last_added_nonpadding = now;
     TO_OR_CONN(conn)->next_circ_id = crypto_rand_int(1<<15);
@@ -759,6 +758,21 @@
 };
 #endif /* HAVE_SYS_UN_H */
 
+static void
+warn_too_many_conns(void)
+{
+#define WARN_TOO_MANY_CONNS_INTERVAL (6*60*60)
+  time_t last_warned = 0, now = time(NULL);
+  int n_conns = get_n_open_sockets();
+  if (last_warned + WARN_TOO_MANY_CONNS_INTERVAL < now) {
+    log_warn(LD_NET,"Failing because we have %d connections already. Please "
+             "raise your ulimit -n.", n_conns);
+    last_warned = now;
+  }
+  control_event_general_status(LOG_WARN, "TOO_MANY_CONNECTIONS CURRENT=%d",
+                               n_conns);
+}
+
 /** Bind a new non-blocking socket listening to the socket described
  * by <b>listensockaddr</b>.
  *
@@ -776,11 +790,7 @@
   int start_reading = 0;
 
   if (get_n_open_sockets() >= get_options()->_ConnLimit-1) {
-    int n_conns = get_n_open_sockets();
-    log_warn(LD_NET,"Failing because we have %d connections already. Please "
-             "raise your ulimit -n.", n_conns);
-    control_event_general_status(LOG_WARN, "TOO_MANY_CONNECTIONS CURRENT=%d",
-                                 n_conns);
+    warn_too_many_conns();
     return NULL;
   }
 
@@ -1034,12 +1044,7 @@
       return 0;
     }
 
-    tor_addr_from_sockaddr(&addr, remote);
-    if (remote->sa_family == AF_INET)
-      port = ((struct sockaddr_in *)remote)->sin_port;
-    else
-      port = ((struct sockaddr_in6 *)remote)->sin6_port;
-    port = ntohs(port);
+    tor_addr_from_sockaddr(&addr, remote, &port);
 
     /* process entrance policies here, before we even create the connection */
     if (new_type == CONN_TYPE_AP) {
@@ -1157,11 +1162,7 @@
   int protocol_family;
 
   if (get_n_open_sockets() >= get_options()->_ConnLimit-1) {
-    int n_conns = get_n_open_sockets();
-    log_warn(LD_NET,"Failing because we have %d connections already. Please "
-             "raise your ulimit -n.", n_conns);
-    control_event_general_status(LOG_WARN, "TOO_MANY_CONNECTIONS CURRENT=%d",
-                                 n_conns);
+    warn_too_many_conns();
     return -1;
   }
 
@@ -2437,28 +2438,6 @@
   }
 }
 
-/** Return the conn to addr/port that has the most recent
- * timestamp_created, or NULL if no such conn exists. */
-or_connection_t *
-connection_or_exact_get_by_addr_port(uint32_t addr, uint16_t port)
-{
-  /* XXXX021 IP6 make this take a tor_addr_t, or deprecate it. */
-
-  or_connection_t *best=NULL;
-  smartlist_t *conns = get_connection_array();
-
-  SMARTLIST_FOREACH(conns, connection_t *, conn,
-  {
-    if (conn->type == CONN_TYPE_OR &&
-        tor_addr_eq_ipv4h(&conn->addr, addr) &&
-        conn->port == port &&
-        !conn->marked_for_close &&
-        (!best || best->_base.timestamp_created < conn->timestamp_created))
-      best = TO_OR_CONN(conn);
-  });
-  return best;
-}
-
 /** Return a connection with given type, address, port, and purpose;
  * or NULL if no such connection exists. */
 connection_t *
@@ -2482,18 +2461,14 @@
 /** Return the stream with id <b>id</b> if it is not already marked for
  * close.
  */
-edge_connection_t *
-connection_get_by_global_id(uint32_t id)
+connection_t *
+connection_get_by_global_id(uint64_t id)
 {
   smartlist_t *conns = get_connection_array();
   SMARTLIST_FOREACH(conns, connection_t *, conn,
   {
-    if (CONN_IS_EDGE(conn) && TO_EDGE_CONN(conn)->global_identifier == id) {
-      if (!conn->marked_for_close)
-        return TO_EDGE_CONN(conn);
-      else
-        return NULL;
-    }
+    if (conn->global_identifier == id)
+      return conn;
   });
   return NULL;
 }

Modified: tor/branches/121-hs-authorization/src/or/connection_edge.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/connection_edge.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/connection_edge.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -14,6 +14,9 @@
 
 #include "or.h"
 
+#ifdef HAVE_LINUX_TYPES_H
+#include <linux/types.h>
+#endif
 #ifdef HAVE_LINUX_NETFILTER_IPV4_H
 #include <linux/netfilter_ipv4.h>
 #define TRANS_NETFILTER
@@ -1618,7 +1621,7 @@
     rep_hist_note_used_internal(now, 0, 1);
 
     /* Look up if we have client authorization for it. */
-    client_auth = lookup_client_auth(conn->rend_query);
+    client_auth = rend_client_lookup_service_authorization(conn->rend_query);
     if (client_auth) {
       log_info(LD_REND, "Using previously configured client authorization "
                "for hidden service.");
@@ -1669,11 +1672,11 @@
 
 #ifdef TRANS_PF
 static int pf_socket = -1;
-static int
+int
 get_pf_socket(void)
 {
   int pf;
-  /*  Ideally, this should be opened before dropping privs. */
+  /*  This should be opened before dropping privs. */
   if (pf_socket >= 0)
     return pf_socket;
 
@@ -1708,9 +1711,9 @@
 {
 #ifdef TRANS_NETFILTER
   /* Linux 2.4+ */
-  struct sockaddr_in orig_dst;
+  struct sockaddr_storage orig_dst;
   socklen_t orig_dst_len = sizeof(orig_dst);
-  char tmpbuf[INET_NTOA_BUF_LEN];
+  tor_addr_t addr;
 
   if (getsockopt(conn->_base.s, SOL_IP, SO_ORIGINAL_DST,
                  (struct sockaddr*)&orig_dst, &orig_dst_len) < 0) {
@@ -1719,16 +1722,16 @@
     return -1;
   }
 
-  tor_inet_ntoa(&orig_dst.sin_addr, tmpbuf, sizeof(tmpbuf));
-  strlcpy(req->address, tmpbuf, sizeof(req->address));
-  req->port = ntohs(orig_dst.sin_port);
+  tor_addr_from_sockaddr(&addr, (struct sockaddr*)&orig_dst, &req->port);
+  tor_addr_to_str(req->address, &addr, sizeof(req->address), 0);
 
   return 0;
 #elif defined(TRANS_PF)
-  struct sockaddr_in proxy_addr;
+  struct sockaddr_storage proxy_addr;
   socklen_t proxy_addr_len = sizeof(proxy_addr);
-  char tmpbuf[INET_NTOA_BUF_LEN];
+  struct sockaddr *proxy_sa = (struct sockaddr*) &proxy_addr;
   struct pfioc_natlook pnl;
+  tor_addr_t addr;
   int pf = -1;
 
   if (getsockname(conn->_base.s, (struct sockaddr*)&proxy_addr,
@@ -1740,13 +1743,28 @@
   }
 
   memset(&pnl, 0, sizeof(pnl));
-  pnl.af              = AF_INET;
   pnl.proto           = IPPROTO_TCP;
   pnl.direction       = PF_OUT;
-  pnl.saddr.v4.s_addr = htonl(conn->_base.addr);
-  pnl.sport           = htons(conn->_base.port);
-  pnl.daddr.v4.s_addr = proxy_addr.sin_addr.s_addr;
-  pnl.dport           = proxy_addr.sin_port;
+  if (proxy_sa->sa_family == AF_INET) {
+    struct sockaddr_in *sin = (struct sockaddr_in *)proxy_sa;
+    pnl.af              = AF_INET;
+    pnl.saddr.v4.s_addr = tor_addr_to_ipv4n(&conn->_base.addr);
+    pnl.sport           = htons(conn->_base.port);
+    pnl.daddr.v4.s_addr = sin->sin_addr.s_addr;
+    pnl.dport           = sin->sin_port;
+  } else if (proxy_sa->sa_family == AF_INET6) {
+    struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)proxy_sa;
+    pnl.af = AF_INET6;
+    memcpy(&pnl.saddr.v6, tor_addr_to_in6(&conn->_base.addr),
+           sizeof(struct in6_addr));
+    pnl.sport = htons(conn->_base.port);
+    memcpy(&pnl.daddr.v6, &sin6->sin6_addr, sizeof(struct in6_addr));
+    pnl.dport = sin6->sin6_port;
+  } else {
+    log_warn(LD_NET, "getsockname() gave an unexpected address family (%d)",
+             (int)proxy_sa->sa_family);
+    return -1;
+  }
 
   pf = get_pf_socket();
   if (pf<0)
@@ -1757,8 +1775,16 @@
     return -1;
   }
 
-  tor_inet_ntoa(&pnl.rdaddr.v4, tmpbuf, sizeof(tmpbuf));
-  strlcpy(req->address, tmpbuf, sizeof(req->address));
+  if (pnl.af == AF_INET) {
+    tor_addr_from_ipv4n(&addr, pnl.rdaddr.v4.s_addr);
+  } else if (pnl.af == AF_INET6) {
+    tor_addr_from_in6(&addr, &pnl.rdaddr.v6);
+  } else {
+    tor_fragile_assert();
+    return -1;
+  }
+
+  tor_addr_to_str(req->address, &addr, sizeof(req->address), 0);
   req->port = ntohs(pnl.rdport);
 
   return 0;

Modified: tor/branches/121-hs-authorization/src/or/connection_or.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/connection_or.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/connection_or.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -837,7 +837,7 @@
   int started_here = connection_or_nonopen_was_started_here(conn);
 
   log_debug(LD_OR,"tls handshake with %s done. verifying.",
-            conn->_base.address);
+            safe_str(conn->_base.address));
 
   directory_set_dirty();
 

Modified: tor/branches/121-hs-authorization/src/or/control.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/control.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/control.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -656,16 +656,16 @@
 static edge_connection_t *
 get_stream(const char *id)
 {
-  uint32_t n_id;
+  uint64_t n_id;
   int ok;
-  edge_connection_t *conn;
-  n_id = (uint32_t) tor_parse_ulong(id, 10, 0, UINT32_MAX, &ok, NULL);
+  connection_t *conn;
+  n_id = tor_parse_uint64(id, 10, 0, UINT64_MAX, &ok, NULL);
   if (!ok)
     return NULL;
   conn = connection_get_by_global_id(n_id);
-  if (!conn || conn->_base.type != CONN_TYPE_AP)
+  if (!conn || conn->type != CONN_TYPE_AP || conn->marked_for_close)
     return NULL;
-  return conn;
+  return TO_EDGE_CONN(conn);
 }
 
 /** Helper for setconf and resetconf. Acts like setconf, except
@@ -1648,8 +1648,7 @@
     smartlist_t *conns = get_connection_array();
     smartlist_t *status = smartlist_create();
     char buf[256];
-    SMARTLIST_FOREACH(conns, connection_t *, base_conn,
-    {
+    SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
       const char *state;
       edge_connection_t *conn;
       char *s;
@@ -1691,12 +1690,12 @@
       slen = strlen(buf)+strlen(state)+32;
       s = tor_malloc(slen+1);
       tor_snprintf(s, slen, "%lu %s %lu %s",
-                   (unsigned long) conn->global_identifier,state,
+                   (unsigned long) conn->_base.global_identifier,state,
                    origin_circ?
                          (unsigned long)origin_circ->global_identifier : 0ul,
                    buf);
       smartlist_add(status, s);
-    });
+    } SMARTLIST_FOREACH_END(base_conn);
     *answer = smartlist_join_strings(status, "\r\n", 0, NULL);
     SMARTLIST_FOREACH(status, char *, cp, tor_free(cp));
     smartlist_free(status);
@@ -3136,8 +3135,8 @@
   if (circ && CIRCUIT_IS_ORIGIN(circ))
     origin_circ = TO_ORIGIN_CIRCUIT(circ);
   send_control_event_extended(EVENT_STREAM_STATUS, ALL_NAMES,
-                        "650 STREAM %lu %s %lu %s@%s%s%s\r\n",
-                        (unsigned long)conn->global_identifier, status,
+                        "650 STREAM "U64_FORMAT" %s %lu %s@%s%s%s\r\n",
+                        U64_PRINTF_ARG(conn->_base.global_identifier), status,
                         origin_circ?
                            (unsigned long)origin_circ->global_identifier : 0ul,
                         buf, reason_buf, addrport_buf, purpose);
@@ -3242,7 +3241,7 @@
     smartlist_t *conns = get_connection_array();
     edge_connection_t *edge_conn;
 
-    SMARTLIST_FOREACH(conns, connection_t *, conn,
+    SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn)
     {
         if (conn->type != CONN_TYPE_AP)
           continue;
@@ -3251,13 +3250,14 @@
           continue;
 
         send_control_event(EVENT_STREAM_BANDWIDTH_USED, ALL_NAMES,
-                            "650 STREAM_BW %lu %lu %lu\r\n",
-                            (unsigned long)edge_conn->global_identifier,
+                            "650 STREAM_BW "U64_FORMAT" %lu %lu\r\n",
+                            U64_PRINTF_ARG(edge_conn->_base.global_identifier),
                             (unsigned long)edge_conn->n_read,
                             (unsigned long)edge_conn->n_written);
 
         edge_conn->n_written = edge_conn->n_read = 0;
-    });
+    }
+    SMARTLIST_FOREACH_END(conn);
   }
 
   return 0;

Modified: tor/branches/121-hs-authorization/src/or/cpuworker.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/cpuworker.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/cpuworker.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -23,7 +23,7 @@
 #define MIN_CPUWORKERS 1
 
 /** The tag specifies which circuit this onionskin was from. */
-#define TAG_LEN 8
+#define TAG_LEN 10
 /** How many bytes are sent from the cpuworker back to tor? */
 #define LEN_ONION_RESPONSE \
   (1+TAG_LEN+ONIONSKIN_REPLY_LEN+CPATH_KEY_MATERIAL_LEN)
@@ -60,33 +60,23 @@
   return 0;
 }
 
-/** Pack addr,port,and circ_id; set *tag to the result. (See note on
+/** Pack global_id and circ_id; set *tag to the result. (See note on
  * cpuworker_main for wire format.) */
 static void
-tag_pack(char *tag, const tor_addr_t *addr, uint16_t port, circid_t circ_id)
+tag_pack(char *tag, uint64_t conn_id, circid_t circ_id)
 {
   /*XXXX RETHINK THIS WHOLE MESS !!!! !NM NM NM NM*/
-  *(uint32_t *)tag     = tor_addr_to_ipv4h(addr);
-  *(uint16_t *)(tag+4) = port;
-  *(uint16_t *)(tag+6) = circ_id;
+  *(uint64_t *)tag     = conn_id;
+  *(uint16_t *)(tag+8) = circ_id;
 }
 
 /** Unpack <b>tag</b> into addr, port, and circ_id.
  */
 static void
-tag_unpack(const char *tag, uint32_t *addr, uint16_t *port, circid_t *circ_id)
+tag_unpack(const char *tag, uint64_t *conn_id, circid_t *circ_id)
 {
-  struct in_addr in;
-  char addrbuf[INET_NTOA_BUF_LEN];
-
-  *addr    = *(const uint32_t *)tag;
-  *port    = *(const uint16_t *)(tag+4);
-  *circ_id = *(const uint16_t *)(tag+6);
-
-  in.s_addr = htonl(*addr);
-  tor_inet_ntoa(&in, addrbuf, sizeof(addrbuf));
-  log_debug(LD_OR,
-            "onion was from %s:%d, circ_id %d.", addrbuf, *port, *circ_id);
+  *conn_id = *(const uint64_t *)tag;
+  *circ_id = *(const uint16_t *)(tag+8);
 }
 
 /** Called when the onion key has changed and we need to spawn new
@@ -136,10 +126,10 @@
 {
   char success;
   char buf[LEN_ONION_RESPONSE];
-  uint32_t addr;
-  uint16_t port;
+  uint64_t conn_id;
   circid_t circ_id;
-  or_connection_t *p_conn;
+  connection_t *tmp_conn;
+  or_connection_t *p_conn = NULL;
   circuit_t *circ;
 
   tor_assert(conn);
@@ -157,14 +147,13 @@
     connection_fetch_from_buf(buf,LEN_ONION_RESPONSE-1,conn);
 
     /* parse out the circ it was talking about */
-    tag_unpack(buf, &addr, &port, &circ_id);
+    tag_unpack(buf, &conn_id, &circ_id);
     circ = NULL;
-    /* (Here we use connection_or_exact_get_by_addr_port rather than
-     * get_by_identity_digest: we want a specific port here in
-     * case there are multiple connections.) */
-    /* XXXX021 This is dumb. We don't want just any connection with a matching
-     * IP and port: we want the exact one that sent us this CREATE cell. */
-    p_conn = connection_or_exact_get_by_addr_port(addr,port);
+    tmp_conn = connection_get_by_global_id(conn_id);
+    if (tmp_conn && !tmp_conn->marked_for_close &&
+        tmp_conn->type == CONN_TYPE_OR)
+      p_conn = TO_OR_CONN(tmp_conn);
+
     if (p_conn)
       circ = circuit_get_by_circid_orconn(circ_id, p_conn);
 
@@ -471,7 +460,7 @@
       tor_free(onionskin);
       return -1;
     }
-    tag_pack(tag, &circ->p_conn->_base.addr, circ->p_conn->_base.port,
+    tag_pack(tag, circ->p_conn->_base.global_identifier,
              circ->p_circ_id);
 
     cpuworker->state = CPUWORKER_STATE_BUSY_ONION;

Modified: tor/branches/121-hs-authorization/src/or/directory.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/directory.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/directory.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -2947,7 +2947,7 @@
   }while(0);
 
   if (!strcmp(url,"/tor/mallinfo.txt") &&
-      (conn->_base.addr == 0x7f000001ul)) {
+      (tor_addr_eq_ipv4h(&conn->_base.addr, 0x7f000001ul))) {
     char *result;
     size_t len;
     struct mallinfo mi;

Modified: tor/branches/121-hs-authorization/src/or/dirserv.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/dirserv.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/dirserv.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -1861,11 +1861,15 @@
 int
 routerstatus_format_entry(char *buf, size_t buf_len,
                           routerstatus_t *rs, const char *version,
-                          int first_line_only)
+                          int first_line_only, int v2_format)
+/* XXX: first_line_only and v2_format should probably be be both
+ *      replaced by a single purpose parameter.
+ */
 {
   int r;
   struct in_addr in;
   char *cp;
+  char *summary;
 
   char published[ISO_TIME_LEN+1];
   char ipaddr[INET_NTOA_BUF_LEN];
@@ -1893,6 +1897,7 @@
   }
   if (first_line_only)
     return 0;
+
   cp = buf + strlen(buf);
   /* NOTE: Whenever this list expands, be sure to increase MAX_FLAG_LINE_LEN*/
   r = tor_snprintf(cp, buf_len - (cp-buf),
@@ -1924,8 +1929,62 @@
       log_warn(LD_BUG, "Unable to print router version.");
       return -1;
     }
+    cp += strlen(cp);
   }
 
+  if (!v2_format) {
+    routerinfo_t* desc = router_get_by_digest(rs->identity_digest);
+
+    /* Blow up more or less nicely if we didn't get anything or not the
+     * thing we expected.
+     */
+    if (!desc) {
+      char id[HEX_DIGEST_LEN+1];
+      char dd[HEX_DIGEST_LEN+1];
+
+      base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN);
+      base16_encode(dd, sizeof(dd), rs->descriptor_digest, DIGEST_LEN);
+      log_warn(LD_BUG, "Cannot get the descriptor with digest %s for %s.",
+               id, dd);
+      return -1;
+    };
+    if (memcmp(desc->cache_info.signed_descriptor_digest,
+               rs->descriptor_digest,
+               DIGEST_LEN)) {
+      char rl_d[HEX_DIGEST_LEN+1];
+      char rs_d[HEX_DIGEST_LEN+1];
+
+      base16_encode(rl_d, sizeof(rl_d),
+                    desc->cache_info.signed_descriptor_digest, DIGEST_LEN);
+      base16_encode(rs_d, sizeof(rs_d), rs->descriptor_digest, DIGEST_LEN);
+      log_err(LD_BUG, "descriptor digest in routerlist does not match "
+                      "the one in routerstatus: %s vs %s\n",
+              rl_d, rs_d);
+
+      tor_assert(!memcmp(desc->cache_info.signed_descriptor_digest,
+                       rs->descriptor_digest,
+                       DIGEST_LEN));
+    };
+
+    r = tor_snprintf(cp, buf_len - (cp-buf),
+                     "w Bandwidth=%d\n",
+                     router_get_advertised_bandwidth_capped(desc) / 1024);
+    if (r<0) {
+      log_warn(LD_BUG, "Not enough space in buffer.");
+      return -1;
+    }
+    cp += strlen(cp);
+
+    summary = policy_summarize(desc->exit_policy);
+    r = tor_snprintf(cp, buf_len - (cp-buf), "p %s\n", summary);
+    if (r<0) {
+      log_warn(LD_BUG, "Not enough space in buffer.");
+      return -1;
+    }
+    cp += strlen(cp);
+    tor_free(summary);
+  }
+
   return 0;
 }
 
@@ -2431,7 +2490,7 @@
       if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest))
         clear_status_flags_on_sybil(&rs);
 
-      if (routerstatus_format_entry(outp, endp-outp, &rs, version, 0)) {
+      if (routerstatus_format_entry(outp, endp-outp, &rs, version, 0, 1)) {
         log_warn(LD_BUG, "Unable to print router status.");
         tor_free(version);
         goto done;

Modified: tor/branches/121-hs-authorization/src/or/dirvote.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/dirvote.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/dirvote.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -106,7 +106,7 @@
     tor_snprintf(status, len,
                  "network-status-version 3\n"
                  "vote-status %s\n"
-                 "consensus-methods 1 2 3 4\n"
+                 "consensus-methods 1 2 3 4 5\n"
                  "published %s\n"
                  "valid-after %s\n"
                  "fresh-until %s\n"
@@ -145,7 +145,7 @@
   SMARTLIST_FOREACH(v3_ns->routerstatus_list, vote_routerstatus_t *, vrs,
   {
     if (routerstatus_format_entry(outp, endp-outp, &vrs->status,
-                                  vrs->version, 0) < 0) {
+                                  vrs->version, 0, 0) < 0) {
       log_warn(LD_BUG, "Unable to print router status.");
       goto err;
     }
@@ -452,7 +452,7 @@
 static int
 consensus_method_is_supported(int method)
 {
-  return (method >= 1) && (method <= 4);
+  return (method >= 1) && (method <= 5);
 }
 
 /** Given a list of vote networkstatus_t in <b>votes</b>, our public
@@ -688,6 +688,9 @@
     smartlist_t *matching_descs = smartlist_create();
     smartlist_t *chosen_flags = smartlist_create();
     smartlist_t *versions = smartlist_create();
+    smartlist_t *exitsummaries = smartlist_create();
+    uint32_t *bandwidths = tor_malloc(sizeof(uint32_t) * smartlist_len(votes));
+    int num_bandwidths;
 
     int *n_voter_flags; /* n_voter_flags[j] is the number of flags that
                          * votes[j] knows about. */
@@ -797,6 +800,7 @@
       const char *lowest_id = NULL;
       const char *chosen_version;
       const char *chosen_name = NULL;
+      int exitsummary_disagreement = 0;
       int is_named = 0, is_unnamed = 0, is_running = 0;
       int naming_conflict = 0;
       int n_listing = 0;
@@ -819,6 +823,7 @@
       smartlist_clear(matching_descs);
       smartlist_clear(chosen_flags);
       smartlist_clear(versions);
+      num_bandwidths = 0;
 
       /* Okay, go through all the entries for this digest. */
       SMARTLIST_FOREACH(votes, networkstatus_t *, v, {
@@ -850,6 +855,10 @@
           }
           chosen_name = rs->status.nickname;
         }
+
+        /* count bandwidths */
+        if (rs->status.has_bandwidth)
+          bandwidths[num_bandwidths++] = rs->status.bandwidth;
       });
 
       /* We don't include this router at all unless more than half of
@@ -922,9 +931,99 @@
         chosen_version = NULL;
       }
 
+      /* Pick a bandwidth */
+      if (consensus_method >= 5 && num_bandwidths > 0) {
+        rs_out.has_bandwidth = 1;
+        rs_out.bandwidth = median_uint32(bandwidths, num_bandwidths);
+      }
+
+      /* Ok, we already picked a descriptor digest we want to list
+       * previously.  Now we want to use the exit policy summary from
+       * that descriptor.  If everybody plays nice all the voters who
+       * listed that descriptor will have the same summary.  If not then
+       * something is fishy and we'll use the most common one (breaking
+       * ties in favor of lexigraphically larger one (only because it
+       * lets me reuse more existing code.
+       *
+       * The other case that can happen is that no authority that voted
+       * for that descriptor has an exit policy summary.  That's
+       * probably quite unlikely but can happen.  In that case we use
+       * the policy that was most often listed in votes, again breaking
+       * ties like in the previous case.
+       */
+      if (consensus_method >= 5) {
+        /* Okay, go through all the votes for this router.  We prepared
+         * that list previously */
+        const char *chosen_exitsummary = NULL;
+        smartlist_clear(exitsummaries);
+        SMARTLIST_FOREACH(matching_descs, vote_routerstatus_t *, vsr, {
+          /* Check if the vote where this status comes from had the
+           * proper descriptor */
+          tor_assert(!memcmp(rs_out.identity_digest,
+                             vsr->status.identity_digest,
+                             DIGEST_LEN));
+          if (vsr->status.has_exitsummary &&
+               !memcmp(rs_out.descriptor_digest,
+                       vsr->status.descriptor_digest,
+                       DIGEST_LEN)) {
+            tor_assert(vsr->status.exitsummary);
+            smartlist_add(exitsummaries, vsr->status.exitsummary);
+            if (!chosen_exitsummary) {
+              chosen_exitsummary = vsr->status.exitsummary;
+            } else if (strcmp(chosen_exitsummary, vsr->status.exitsummary)) {
+              /* Great.  There's disagreement among the voters.  That
+               * really shouldn't be */
+              exitsummary_disagreement = 1;
+            }
+          }
+        });
+
+        if (exitsummary_disagreement) {
+          char id[HEX_DIGEST_LEN+1];
+          char dd[HEX_DIGEST_LEN+1];
+          base16_encode(id, sizeof(dd), rs_out.identity_digest, DIGEST_LEN);
+          base16_encode(dd, sizeof(dd), rs_out.descriptor_digest, DIGEST_LEN);
+          log_warn(LD_DIR, "The voters disgreed on the exit policy summary for"
+                   " router %s with descriptor %s.  This really shouldn't"
+                   " have happened.", id, dd);
+
+          smartlist_sort_strings(exitsummaries);
+          chosen_exitsummary = get_most_frequent_member(exitsummaries);
+        } else if (!chosen_exitsummary) {
+          char id[HEX_DIGEST_LEN+1];
+          char dd[HEX_DIGEST_LEN+1];
+          base16_encode(id, sizeof(dd), rs_out.identity_digest, DIGEST_LEN);
+          base16_encode(dd, sizeof(dd), rs_out.descriptor_digest, DIGEST_LEN);
+          log_warn(LD_DIR, "Not one of the voters that made us select"
+                   "descriptor %s for router %s had an exit policy"
+                   "summary", dd, id);
+
+          /* Ok, none of those voting for the digest we chose had an
+           * exit policy for us.  Well, that kinda sucks.
+           */
+          smartlist_clear(exitsummaries);
+          SMARTLIST_FOREACH(matching_descs, vote_routerstatus_t *, vsr, {
+            if (vsr->status.has_exitsummary)
+              smartlist_add(exitsummaries, vsr->status.exitsummary);
+          });
+          smartlist_sort_strings(exitsummaries);
+          chosen_exitsummary = get_most_frequent_member(exitsummaries);
+
+          if (!chosen_exitsummary)
+            log_warn(LD_DIR, "Wow, not one of the voters had an exit "
+                     "policy summary for %s.  Wow.", id);
+        }
+
+        if (chosen_exitsummary) {
+          rs_out.has_exitsummary = 1;
+          /* yea, discards the const */
+          rs_out.exitsummary = (char *)chosen_exitsummary;
+        }
+      }
+
       /* Okay!! Now we can write the descriptor... */
       /*     First line goes into "buf". */
-      routerstatus_format_entry(buf, sizeof(buf), &rs_out, NULL, 1);
+      routerstatus_format_entry(buf, sizeof(buf), &rs_out, NULL, 1, 0);
       smartlist_add(chunks, tor_strdup(buf));
       /*     Second line is all flags.  The "\n" is missing. */
       smartlist_add(chunks,
@@ -935,6 +1034,25 @@
         smartlist_add(chunks, tor_strdup(chosen_version));
       }
       smartlist_add(chunks, tor_strdup("\n"));
+      /*     Now the weight line. */
+      if (rs_out.has_bandwidth) {
+        int r = tor_snprintf(buf, sizeof(buf),
+                             "w Bandwidth=%d\n", rs_out.bandwidth);
+        if (r<0) {
+          log_warn(LD_BUG, "Not enough space in buffer for weight line.");
+          *buf = '\0';
+        }
+        smartlist_add(chunks, tor_strdup(buf));
+      };
+      /*     Now the exitpolicy summary line. */
+      if (rs_out.has_exitsummary) {
+        int r = tor_snprintf(buf, sizeof(buf), "p %s\n", rs_out.exitsummary);
+        if (r<0) {
+          log_warn(LD_BUG, "Not enough space in buffer for exitpolicy line.");
+          *buf = '\0';
+        }
+        smartlist_add(chunks, tor_strdup(buf));
+      };
 
       /* And the loop is over and we move on to the next router */
     }
@@ -953,6 +1071,7 @@
     smartlist_free(matching_descs);
     smartlist_free(chosen_flags);
     smartlist_free(versions);
+    smartlist_free(exitsummaries);
   }
 
   /* Add a signature. */

Modified: tor/branches/121-hs-authorization/src/or/dnsserv.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/dnsserv.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/dnsserv.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -46,7 +46,7 @@
   }
   (void) addrlen;
   sa = (struct sockaddr*) &addr;
-  if (tor_addr_from_sockaddr(&tor_addr, sa)<0) {
+  if (tor_addr_from_sockaddr(&tor_addr, sa, &port)<0) {
     log_warn(LD_APP, "Requesting address wasn't recognized.");
     evdns_server_request_respond(req, DNS_ERR_SERVERFAILED);
     return;
@@ -58,12 +58,6 @@
     return;
   }
 
-  if (sa->sa_family == AF_INET)
-    port = ((struct sockaddr_in *)sa)->sin_port;
-  else
-    port = ((struct sockaddr_in6 *)sa)->sin6_port;
-  port = ntohs(port);
-
   /* Now, let's find the first actual question of a type we can answer in this
    * DNS request.  It makes us a little noncompliant to act like this; we
    * should fix that eventually if it turns out to make a difference for

Modified: tor/branches/121-hs-authorization/src/or/main.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/main.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/main.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -1873,6 +1873,7 @@
   dirserv_free_all();
   rend_service_free_all();
   rend_cache_free_all();
+  rend_service_authorization_free_all();
   rep_hist_free_all();
   hs_usage_free_all();
   dns_free_all();

Modified: tor/branches/121-hs-authorization/src/or/networkstatus.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/networkstatus.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/networkstatus.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -1779,7 +1779,7 @@
 networkstatus_getinfo_helper_single(routerstatus_t *rs)
 {
   char buf[RS_ENTRY_LEN+1];
-  routerstatus_format_entry(buf, sizeof(buf), rs, NULL, 0);
+  routerstatus_format_entry(buf, sizeof(buf), rs, NULL, 0, 0);
   return tor_strdup(buf);
 }
 

Modified: tor/branches/121-hs-authorization/src/or/or.h
===================================================================
--- tor/branches/121-hs-authorization/src/or/or.h	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/or.h	2008-08-16 11:36:09 UTC (rev 16566)
@@ -612,6 +612,9 @@
 /** Length of 'y' portion of 'y.onion' URL. */
 #define REND_SERVICE_ID_LEN_BASE32 16
 
+/** Length of 'y.onion' including '.onion' URL. */
+#define REND_SERVICE_ADDRESS_LEN (16+1+5)
+
 /** Length of a binary-encoded rendezvous service ID. */
 #define REND_SERVICE_ID_LEN 10
 
@@ -656,6 +659,9 @@
 #define REND_LEGAL_CLIENTNAME_CHARACTERS \
   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-_"
 
+/** Maximum length of authorized client names for a hidden service. */
+#define REND_CLIENTNAME_MAX_LEN 16
+
 #define CELL_DIRECTION_IN 1
 #define CELL_DIRECTION_OUT 2
 
@@ -901,6 +907,9 @@
   /** Another connection that's connected to this one in lieu of a socket. */
   struct connection_t *linked_conn;
 
+  /** Unique identifier for this connection. */
+  uint64_t global_identifier;
+
   /* XXXX021 move this into a subtype. */
   struct evdns_server_port *dns_server_port;
 
@@ -1011,10 +1020,6 @@
   /** The reason why this connection is closing; passed to the controller. */
   uint16_t end_reason;
 
-  /** Quasi-global identifier for this connection; used for control.c */
-  /* XXXX NM This can get re-used after 2**32 streams */
-  uint32_t global_identifier;
-
   /** Bytes read since last call to control_event_stream_bandwidth_used() */
   uint32_t n_read;
 
@@ -1413,7 +1418,7 @@
                                * an exit node. */
   unsigned int is_bad_directory:1; /**< Do we think this directory is junky,
                                     * underpowered, or otherwise useless? */
-  unsigned int is_hs_dir:1; /** True iff this router is a v2-or-later hidden
+  unsigned int is_hs_dir:1; /**< True iff this router is a v2-or-later hidden
                              * service directory. */
   /** True iff we know version info for this router. (i.e., a "v" entry was
    * included.)  We'll replace all these with a big tor_version_t or a char[]
@@ -1430,6 +1435,14 @@
    * we can get v3 downloads from. */
   unsigned int version_supports_v3_dir:1;
 
+  unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */
+  unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */
+
+  uint32_t bandwidth; /**< Bandwidth (capacity) of the router as reported in
+                       * the vote/consensus, in kilobytes/sec. */
+  char *exitsummary; /**< exit policy summary -
+                      * XXX weasel: this probably should not stay a string. */
+
   /* ---- The fields below aren't derived from the networkstatus; they
    * hold local information only. */
 
@@ -2266,8 +2279,8 @@
                    * other ORs are running. */
   config_line_t *RendConfigLines; /**< List of configuration lines
                                           * for rendezvous services. */
-  config_line_t *ClientSideHidServs; /** List of configuration lines for
-                        * client-side authorizations for hidden services */
+  config_line_t *HidServAuth; /**< List of configuration lines for client-side
+                               * authorizations for hidden services */
   char *ContactInfo; /**< Contact info to be published in the directory. */
 
   char *HttpProxy; /**< hostname[:port] to use as http proxy, if any. */
@@ -2874,9 +2887,7 @@
   _connection_write_to_buf_impl(string, len, TO_CONN(conn), done ? -1 : 1);
 }
 
-or_connection_t *connection_or_exact_get_by_addr_port(uint32_t addr,
-                                                   uint16_t port);
-edge_connection_t *connection_get_by_global_id(uint32_t id);
+connection_t *connection_get_by_global_id(uint64_t id);
 
 connection_t *connection_get_by_type(int type);
 connection_t *connection_get_by_type_purpose(int type, int purpose);
@@ -2982,6 +2993,10 @@
 } hostname_type_t;
 hostname_type_t parse_extended_hostname(char *address);
 
+#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H)
+int get_pf_socket(void);
+#endif
+
 /********************************* connection_or.c ***************************/
 
 void connection_or_remove_from_identity_map(or_connection_t *conn);
@@ -3228,11 +3243,19 @@
 }
 
 /********************************* dirserv.c ***************************/
+/** Maximum length of an exit policy summary. */
+#define MAX_EXITPOLICY_SUMMARY_LEN (1000)
+
 /** Maximum allowable length of a version line in a networkstatus. */
 #define MAX_V_LINE_LEN 128
 /** Length of "r Authority BadDirectory BadExit Exit Fast Guard HSDir Named
  * Running Stable Unnamed V2Dir Valid\n". */
 #define MAX_FLAG_LINE_LEN 96
+/** Length of "w" line for weighting.  Currently at most
+ * "w Bandwidth=<uint32t>\n" */
+#define MAX_WEIGHT_LINE_LEN (13+10)
+/** Maximum length of an exit policy summary line. */
+#define MAX_POLICY_LINE_LEN (3+MAX_EXITPOLICY_SUMMARY_LEN)
 /** Amount of space to allocate for each entry: r, s, and v lines. */
 #define RS_ENTRY_LEN                                                    \
   ( /* first line */                                                    \
@@ -3240,6 +3263,10 @@
    5*2 /* ports */ + 10 /* punctuation */ +                             \
    /* second line */                                                    \
    MAX_FLAG_LINE_LEN +                                                  \
+   /* weight line */                                                    \
+   MAX_WEIGHT_LINE_LEN +                                                \
+   /* p line. */                                                        \
+   MAX_POLICY_LINE_LEN +                                                \
    /* v line. */                                                        \
    MAX_V_LINE_LEN                                                       \
    )
@@ -3309,7 +3336,7 @@
                                   int compressed);
 int routerstatus_format_entry(char *buf, size_t buf_len,
                               routerstatus_t *rs, const char *platform,
-                              int first_line_only);
+                              int first_line_only, int v2_format);
 void dirserv_free_all(void);
 void cached_dir_decref(cached_dir_t *d);
 cached_dir_t *new_cached_dir(char *s, time_t published);
@@ -3686,6 +3713,8 @@
 void addr_policy_free(addr_policy_t *p);
 void policies_free_all(void);
 
+char *policy_summarize(smartlist_t *policy);
+
 /********************************* reasons.c ***************************/
 
 const char *stream_end_reason_to_control_string(int reason);
@@ -3832,25 +3861,36 @@
 
 int rend_client_send_introduction(origin_circuit_t *introcirc,
                                   origin_circuit_t *rendcirc);
-int rend_parse_client_auth(char *config_line);
 
+/** Client authorization type that a hidden service performs. */
+typedef enum rend_auth_type_t {
+  REND_NO_AUTH      = 0,
+  REND_BASIC_AUTH   = 1,
+  REND_STEALTH_AUTH = 2,
+} rend_auth_type_t;
+
+/** Client-side configuration of authorization for a hidden service. */
+typedef struct rend_service_authorization_t {
+  char descriptor_cookie[REND_DESC_COOKIE_LEN];
+  char onion_address[REND_SERVICE_ADDRESS_LEN+1];
+  rend_auth_type_t auth_type;
+} rend_service_authorization_t;
+
+int rend_parse_service_authorization(or_options_t *options,
+                                     int validate_only);
+rend_service_authorization_t *rend_client_lookup_service_authorization(
+                                                const char *onion_address);
+void rend_service_authorization_free_all(void);
+
 /********************************* rendcommon.c ***************************/
 
-/** Hidden-service-side configuration of client authorization. */
+/** Hidden-service side configuration of client authorization. */
 typedef struct rend_authorized_client_t {
   char *client_name;
   char descriptor_cookie[REND_DESC_COOKIE_LEN];
   crypto_pk_env_t *client_key;
 } rend_authorized_client_t;
 
-/** Client-side configuration of authorization for a hidden service. */
-typedef struct rend_service_authorization_t {
-  char *service_name;
-  char descriptor_cookie[REND_DESC_COOKIE_LEN];
-  char onion_address[REND_SERVICE_ID_LEN_BASE32+1+5+1];
-  int auth_type;
-} rend_service_authorization_t;
-
 /** ASCII-encoded v2 hidden service descriptor. */
 typedef struct rend_encoded_v2_service_descriptor_t {
   char desc_id[DIGEST_LEN]; /**< Descriptor ID. */
@@ -3928,7 +3968,6 @@
 void rend_get_descriptor_id_bytes(char *descriptor_id_out,
                                   const char *service_id,
                                   const char *secret_id_part);
-rend_service_authorization_t *lookup_client_auth(char *onion_address);
 
 /********************************* rendservice.c ***************************/
 
@@ -4120,6 +4159,7 @@
 int router_is_unreliable(routerinfo_t *router, int need_uptime,
                          int need_capacity, int need_guard);
 uint32_t router_get_advertised_bandwidth(routerinfo_t *router);
+uint32_t router_get_advertised_bandwidth_capped(routerinfo_t *router);
 
 typedef enum {
   NO_WEIGHTING, WEIGHT_FOR_EXIT, WEIGHT_FOR_GUARD

Modified: tor/branches/121-hs-authorization/src/or/policies.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/policies.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/policies.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -38,16 +38,32 @@
  * to directories at. */
 static smartlist_t *reachable_dir_addr_policy = NULL;
 
+/** Element of an exit policy summary */
+typedef struct policy_summary_item_t {
+    uint16_t prt_min; /**< Lowest port number to accept/reject. */
+    uint16_t prt_max; /**< Highest port number to accept/reject. */
+    uint64_t reject_count; /**< Number of IP-Addresses that are rejected to
+                                this portrange. */
+    int accepted:1; /** Has this port already been accepted */
+} policy_summary_item_t;
+
+/** Private networks.  This list is used in two places, once to expand the
+ *  "private" keyword when parsing our own exit policy, secondly to ignore
+ *  just such networks when building exit policy summaries.  It is important
+ *  that all authorities agree on that list when creating summaries, so don't
+ *  just change this without a proper migration plan and a proposal and stuff.
+ */
+static const char *private_nets[] = {
+  "0.0.0.0/8", "169.254.0.0/16",
+  "127.0.0.0/8", "192.168.0.0/16", "10.0.0.0/8", "172.16.0.0/12",
+  // "fc00::/7", "fe80::/10", "fec0::/10", "::/127",
+  NULL };
+
 /** Replace all "private" entries in *<b>policy</b> with their expanded
  * equivalents. */
 void
 policy_expand_private(smartlist_t **policy)
 {
-  static const char *private_nets[] = {
-    "0.0.0.0/8", "169.254.0.0/16",
-    "127.0.0.0/8", "192.168.0.0/16", "10.0.0.0/8", "172.16.0.0/12",
-    // "fc00::/7", "fe80::/10", "fec0::/10", "::/127",
-    NULL };
   uint16_t port_min, port_max;
 
   int i;
@@ -904,6 +920,280 @@
   return (int)written;
 }
 
+/** Create a new exit policy summary, initially only with a single
+ *  port 1-64k item */
+/* XXXX This entire thing will do most stuff in O(N^2), or worse.  Use an
+ *      RB-tree if that turns out to matter. */
+static smartlist_t *
+policy_summary_create(void)
+{
+  smartlist_t *summary;
+  policy_summary_item_t* item;
+
+  item = tor_malloc_zero(sizeof(policy_summary_item_t));
+  item->prt_min = 1;
+  item->prt_max = 65535;
+  item->reject_count = 0;
+  item->accepted = 0;
+
+  summary = smartlist_create();
+  smartlist_add(summary, item);
+
+  return summary;
+}
+
+/** Split the summary item in <b>item</b> at the port <b>new_starts</b>.
+ * The current item is changed to end at new-starts - 1, the new item
+ * copies reject_count and accepted from the old item,
+ * starts at new_starts and ends at the port where the original item
+ * previously ended.
+ */
+static policy_summary_item_t*
+policy_summary_item_split(policy_summary_item_t* old, uint16_t new_starts)
+{
+  policy_summary_item_t* new;
+
+  new = tor_malloc_zero(sizeof(policy_summary_item_t));
+  new->prt_min = new_starts;
+  new->prt_max = old->prt_max;
+  new->reject_count = old->reject_count;
+  new->accepted = old->accepted;
+
+  old->prt_max = new_starts-1;
+
+  tor_assert(old->prt_min <= old->prt_max);
+  tor_assert(new->prt_min <= new->prt_max);
+  return new;
+}
+
+/* XXXX Nick says I'm going to hell for this.  If he feels charitably towards
+ * my immortal soul, he can clean it up himself. */
+#define AT(x) ((policy_summary_item_t*)smartlist_get(summary, x))
+
+#define REJECT_CUTOFF_COUNT (1<<25)
+/* Split an exit policy summary so that prt_min and prt_max
+ * fall at exactly the start and end of an item respectively.
+ */
+static int
+policy_summary_split(smartlist_t *summary,
+                     uint16_t prt_min, uint16_t prt_max)
+{
+  int start_at_index;
+
+  int i = 0;
+  /* XXXX Do a binary search if run time matters */
+  while (AT(i)->prt_max < prt_min)
+    i++;
+  if (AT(i)->prt_min != prt_min) {
+    policy_summary_item_t* new_item;
+    new_item = policy_summary_item_split(AT(i), prt_min);
+    smartlist_insert(summary, i+1, new_item);
+    i++;
+  }
+  start_at_index = i;
+
+  while (AT(i)->prt_max < prt_max)
+    i++;
+  if (AT(i)->prt_max != prt_max) {
+    policy_summary_item_t* new_item;
+    new_item = policy_summary_item_split(AT(i), prt_max+1);
+    smartlist_insert(summary, i+1, new_item);
+  }
+
+  return start_at_index;
+}
+
+/** Mark port ranges as accepted if they are below the reject_count */
+static void
+policy_summary_accept(smartlist_t *summary,
+                      uint16_t prt_min, uint16_t prt_max)
+{
+  int i = policy_summary_split(summary, prt_min, prt_max);
+  while (i < smartlist_len(summary) &&
+         AT(i)->prt_max <= prt_max) {
+    if (!AT(i)->accepted &&
+        AT(i)->reject_count <= REJECT_CUTOFF_COUNT)
+      AT(i)->accepted = 1;
+    i++;
+  }
+  tor_assert(i < smartlist_len(summary) || prt_max==65535);
+}
+
+/** Count the number of addresses in a network with prefixlen maskbits
+ * against the given portrange. */
+static void
+policy_summary_reject(smartlist_t *summary,
+                      maskbits_t maskbits,
+                      uint16_t prt_min, uint16_t prt_max)
+{
+  int i = policy_summary_split(summary, prt_min, prt_max);
+  /* XXX: ipv4 specific */
+  uint64_t count = (U64_LITERAL(1) << (32-maskbits));
+  while (i < smartlist_len(summary) &&
+         AT(i)->prt_max <= prt_max) {
+    AT(i)->reject_count += count;
+    i++;
+  }
+  tor_assert(i < smartlist_len(summary) || prt_max==65535);
+}
+
+/** Add a single exit policy item to our summary:
+ *  If it is an accept ignore it unless it is for all IP addresses
+ *  ("*"), i.e. it's prefixlen/maskbits is 0, else call
+ *  policy_summary_accept().
+ *  If it's a reject ignore it if it is about one of the private
+ *  networks, else call policy_summary_reject().
+ */
+static void
+policy_summary_add_item(smartlist_t *summary, addr_policy_t *p)
+{
+  if (p->policy_type == ADDR_POLICY_ACCEPT) {
+    if (p->maskbits == 0) {
+      policy_summary_accept(summary, p->prt_min, p->prt_max);
+    }
+  } else if (p->policy_type == ADDR_POLICY_REJECT) {
+
+     int is_private = 0;
+     int i;
+     for (i = 0; private_nets[i]; ++i) {
+       tor_addr_t addr;
+       maskbits_t maskbits;
+       if (tor_addr_parse_mask_ports(private_nets[i], &addr,
+                                  &maskbits, NULL, NULL)<0) {
+         tor_assert(0);
+       }
+       if (tor_addr_compare(&p->addr, &addr, CMP_EXACT) == 0 &&
+           p->maskbits == maskbits) {
+         is_private = 1;
+         break;
+       }
+     }
+
+     if (!is_private) {
+       policy_summary_reject(summary, p->maskbits, p->prt_min, p->prt_max);
+     }
+  } else
+    tor_assert(0);
+}
+
+/** Create a string representing a summary for an exit policy.
+ * The summary will either be an "accept" plus a comma-seperated list of port
+ * ranges or a "reject" plus portranges, depending on which is shorter.
+ *
+ * If no exits are allowed at all then NULL is returned, if no ports
+ * are blocked instead of "reject " we return "accept 1-65535" (this
+ * is an exception to the shorter-representation-wins rule).
+ */
+char *
+policy_summarize(smartlist_t *policy)
+{
+  smartlist_t *summary = policy_summary_create();
+  smartlist_t *accepts, *rejects;
+  int i, last, start_prt;
+  size_t accepts_len, rejects_len, shorter_len, final_size;
+  char *accepts_str = NULL, *rejects_str = NULL, *shorter_str, *result;
+  const char *prefix;
+
+  tor_assert(policy);
+
+  /* Create the summary list */
+  SMARTLIST_FOREACH(policy, addr_policy_t *, p, {
+    policy_summary_add_item(summary, p);
+  });
+
+  /* Now create two lists of strings, one for accepted and one
+   * for rejected ports.  We take care to merge ranges so that
+   * we avoid getting stuff like "1-4,5-9,10", instead we want
+   * "1-10"
+   */
+  i = 0;
+  start_prt = 1;
+  accepts = smartlist_create();
+  rejects = smartlist_create();
+  while (1) {
+    last = i == smartlist_len(summary)-1;
+    if (last ||
+        AT(i)->accepted != AT(i+1)->accepted) {
+      char buf[POLICY_BUF_LEN];
+
+      if (start_prt == AT(i)->prt_max)
+        tor_snprintf(buf, sizeof(buf), "%d", start_prt);
+      else
+        tor_snprintf(buf, sizeof(buf), "%d-%d", start_prt, AT(i)->prt_max);
+
+      if (AT(i)->accepted)
+        smartlist_add(accepts, tor_strdup(buf));
+      else
+        smartlist_add(rejects, tor_strdup(buf));
+
+      if (last)
+        break;
+
+      start_prt = AT(i+1)->prt_min;
+    };
+    i++;
+  };
+
+  /* Figure out which of the two stringlists will be shorter and use
+   * that to build the result
+   */
+  if (smartlist_len(accepts) == 0) { /* no exits at all */
+    result = tor_strdup("reject 1-65535");
+    goto cleanup;
+  }
+  if (smartlist_len(rejects) == 0) { /* no rejects at all */
+    result = tor_strdup("accept 1-65535");
+    goto cleanup;
+  }
+
+  accepts_str = smartlist_join_strings(accepts, ",", 0, &accepts_len);
+  rejects_str = smartlist_join_strings(rejects, ",", 0, &rejects_len);
+
+  if (rejects_len > MAX_EXITPOLICY_SUMMARY_LEN &&
+      accepts_len > MAX_EXITPOLICY_SUMMARY_LEN) {
+    char *c;
+    shorter_str = accepts_str;
+    prefix = "accept";
+
+    c = shorter_str + (MAX_EXITPOLICY_SUMMARY_LEN-strlen(prefix)-1);
+    while (*c != ',' && c >= shorter_str)
+      c--;
+    tor_assert(c >= shorter_str);
+    tor_assert(*c == ',');
+    *c = '\0';
+
+    shorter_len = strlen(shorter_str);
+  } else if (rejects_len < accepts_len) {
+    shorter_str = rejects_str;
+    shorter_len = rejects_len;
+    prefix = "reject";
+  } else {
+    shorter_str = accepts_str;
+    shorter_len = accepts_len;
+    prefix = "accept";
+  }
+
+  final_size = strlen(prefix)+1+shorter_len+1;
+  tor_assert(final_size <= MAX_EXITPOLICY_SUMMARY_LEN+1);
+  result = malloc(final_size);
+  tor_snprintf(result, final_size, "%s %s", prefix, shorter_str);
+
+cleanup:
+  /* cleanup */
+  SMARTLIST_FOREACH(summary, policy_summary_item_t *, s, tor_free(s));
+  smartlist_clear(summary);
+
+  tor_free(accepts_str);
+  SMARTLIST_FOREACH(accepts, char *, s, tor_free(s));
+  smartlist_clear(accepts);
+
+  tor_free(rejects_str);
+  SMARTLIST_FOREACH(rejects, char *, s, tor_free(s));
+  smartlist_clear(rejects);
+
+  return result;
+}
+
 /** Implementation for GETINFO control command: knows the answer for questions
  * about "exit-policy/..." */
 int

Modified: tor/branches/121-hs-authorization/src/or/rendclient.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/rendclient.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/rendclient.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -20,7 +20,7 @@
   tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
   tor_assert(circ->cpath);
 
-  log_info(LD_REND, "introcirc is open");
+  log_info(LD_REND,"introcirc is open");
   connection_ap_attach_pending();
 }
 
@@ -773,7 +773,7 @@
  * <b>onion_address</b>. Return NULL if no authorization is available for
  * that address. */
 rend_service_authorization_t*
-lookup_client_auth(char *onion_address)
+rend_client_lookup_service_authorization(const char *onion_address)
 {
   tor_assert(onion_address);
   if (!auth_hid_servs) return NULL;
@@ -784,104 +784,117 @@
 static void
 rend_service_authorization_free(rend_service_authorization_t *auth)
 {
-  if (!auth) return;
-  tor_free(auth->service_name);
   tor_free(auth);
 }
 
+/** Helper for strmap_free. */
+static void
+rend_service_authorization_strmap_item_free(void *service_auth)
+{
+  rend_service_authorization_free(service_auth);
+}
+
+/** Release all the storage held in auth_hid_servs.
+ */
+void
+rend_service_authorization_free_all(void)
+{
+  if (!auth_hid_servs) {
+    return;
+  }
+  strmap_free(auth_hid_servs, rend_service_authorization_strmap_item_free);
+  auth_hid_servs = NULL;
+}
+
 /** Parse <b>config_line</b> as a client-side authorization for a hidden
  * service and add it to the local map of hidden service authorizations.
- * Return 1 for success and 0 for failure. */
+ * Return 0 for success and -1 for failure. */
 int
-rend_parse_client_auth(char *config_line)
+rend_parse_service_authorization(or_options_t *options, int validate_only)
 {
-  char *service_name, *onion_address, *descriptor_cookie;
-  char descriptor_cookie_tmp[REND_DESC_COOKIE_LEN+2];
-  char descriptor_cookie_base64ext[REND_DESC_COOKIE_LEN_BASE64+2+1];
+  config_line_t *line;
+  int res = -1;
+  strmap_t *parsed = strmap_new();
   smartlist_t *sl = smartlist_create();
-  rend_service_authorization_t *auth = NULL;
-  int res = 0, auth_type = 0;
-  size_t len;
-  tor_assert(config_line);
-  smartlist_split_string(sl, config_line, " ",
-                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
-  if (smartlist_len(sl) != 3) {
-    log_warn(LD_CONFIG, "Configuration line does not consist of "
-             "\"service-name client-key descriptor-cookie\": '%s'",
-             config_line);
-    goto free;
+
+  for (line = options->HidServAuth; line; line = line->next) {
+    char *onion_address, *descriptor_cookie;
+    char descriptor_cookie_tmp[REND_DESC_COOKIE_LEN+2];
+    char descriptor_cookie_base64ext[REND_DESC_COOKIE_LEN_BASE64+2+1];
+    rend_service_authorization_t *auth = NULL;
+    int auth_type_val = 0;
+    SMARTLIST_FOREACH(sl, char *, c, tor_free(c););
+    smartlist_clear(sl);
+    smartlist_split_string(sl, line->value, " ",
+                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3);
+    if (smartlist_len(sl) < 2) {
+      log_warn(LD_CONFIG, "Configuration line does not consist of "
+               "\"onion-address authorization-cookie [service-name]\": "
+               "'%s'", line->value);
+      goto err;
+    }
+    auth = tor_malloc_zero(sizeof(rend_service_authorization_t));
+    /* Parse onion address. */
+    onion_address = smartlist_get(sl, 0);
+    if (strlen(onion_address) != REND_SERVICE_ADDRESS_LEN ||
+        strcmpend(onion_address, ".onion")) {
+      log_warn(LD_CONFIG, "Onion address has wrong format: '%s'",
+               onion_address);
+      goto err;
+    }
+    strlcpy(auth->onion_address, onion_address, REND_SERVICE_ID_LEN_BASE32+1);
+    if (!rend_valid_service_id(auth->onion_address)) {
+      log_warn(LD_CONFIG, "Onion address has wrong format: '%s'",
+               onion_address);
+      goto err;
+    }
+    /* Parse descriptor cookie. */
+    descriptor_cookie = smartlist_get(sl, 1);
+    if (strlen(descriptor_cookie) != REND_DESC_COOKIE_LEN_BASE64) {
+      log_warn(LD_CONFIG, "Authorization cookie has wrong length: '%s'",
+               descriptor_cookie);
+      goto err;
+    }
+    /* Add trailing zero bytes (AA) to make base64-decoding happy. */
+    tor_snprintf(descriptor_cookie_base64ext,
+                 REND_DESC_COOKIE_LEN_BASE64+2+1,
+                 "%sAA", descriptor_cookie);
+    if (base64_decode(descriptor_cookie_tmp, sizeof(descriptor_cookie_tmp),
+                      descriptor_cookie_base64ext,
+                      strlen(descriptor_cookie_base64ext)) < 0) {
+      log_warn(LD_CONFIG, "Decoding authorization cookie failed: '%s'",
+               descriptor_cookie);
+      goto err;
+    }
+    auth_type_val = (descriptor_cookie_tmp[16] >> 4) + 1;
+    if (auth_type_val < 1 || auth_type_val > 2) {
+      log_warn(LD_CONFIG, "Authorization cookie has unknown authorization "
+                          "type encoded.");
+      goto err;
+    }
+    auth->auth_type = auth_type_val == 1 ? REND_BASIC_AUTH : REND_STEALTH_AUTH;
+    memcpy(auth->descriptor_cookie, descriptor_cookie_tmp,
+           REND_DESC_COOKIE_LEN);
+    if (strmap_get(parsed, auth->onion_address)) {
+      log_warn(LD_CONFIG, "Duplicate authorization for the same hidden "
+                          "service.");
+      goto err;
+    }
+    strmap_set(parsed, auth->onion_address, auth);
   }
-  /* Parse service name (rather meant for use in a GUI controller). */
-  service_name = smartlist_get(sl, 0);
-  auth = tor_malloc_zero(sizeof(rend_service_authorization_t));
-  auth->service_name = strdup(smartlist_get(sl, 0));
-  len = strlen(auth->service_name);
-  if (len < 1 || len > 19 ||
-      strspn(auth->service_name, REND_LEGAL_CLIENTNAME_CHARACTERS) != len) {
-    log_warn(LD_CONFIG, "HidServAuth contains an illegal service name: "
-                        "'%s'. (Length must be between 1 and 19, and "
-                        "valid characters are [A-Za-z0-9+-_].)",
-             auth->service_name);
-    goto free;
+  res = 0;
+  goto done;
+ err:
+  res = -1;
+ done:
+  SMARTLIST_FOREACH(sl, char *, c, tor_free(c););
+  smartlist_free(sl);
+  if (!validate_only && res == 0) {
+    rend_service_authorization_free_all();
+    auth_hid_servs = parsed;
+  } else {
+    strmap_free(parsed, rend_service_authorization_strmap_item_free);
   }
-  if (auth_hid_servs && strmap_get(auth_hid_servs, auth->service_name)) {
-    log_warn(LD_CONFIG, "Duplicate service name for configuration line: "
-                        "'%s'", auth->service_name);
-    goto free;
-  }
-  /* Parse onion address. */
-  onion_address = smartlist_get(sl, 1);
-  if (strlen(onion_address) != 16+1+5 ||
-      strstr(onion_address, ".onion") != onion_address + 16) {
-    log_warn(LD_CONFIG, "Onion address has wrong format: '%s'",
-             onion_address);
-    goto free;
-  }
-  strlcpy(auth->onion_address, onion_address, 16+1);
-  if (!rend_valid_service_id(auth->onion_address)) {
-    log_warn(LD_CONFIG, "Onion address has wrong format: '%s'",
-             onion_address);
-    goto free;
-  }
-  /* Parse descriptor cookie. */
-  descriptor_cookie = smartlist_get(sl, 2);
-  if (strlen(descriptor_cookie) != 22) {
-    log_warn(LD_CONFIG, "Descriptor cookie has wrong length: '%s'",
-             descriptor_cookie);
-    goto free;
-  }
-  /* Add trailing zero bytes (AA) to make base64-decoding happy. */
-  tor_snprintf(descriptor_cookie_base64ext,
-               REND_DESC_COOKIE_LEN_BASE64+2+1,
-               "%sAA", descriptor_cookie);
-  if (base64_decode(descriptor_cookie_tmp, REND_DESC_COOKIE_LEN+2,
-                    descriptor_cookie_base64ext,
-                    strlen(descriptor_cookie_base64ext)) < 0) {
-    log_warn(LD_CONFIG, "Decoding descriptor cookie failed: '%s'",
-             descriptor_cookie);
-    goto free;
-  }
-  auth_type = (descriptor_cookie_tmp[16] >> 4) + 1;
-  if (auth_type < 1 || auth_type > 2) {
-    log_warn(LD_CONFIG, "Descriptor cookie has unknown authorization type "
-             "encoded.");
-    goto free;
-  }
-  auth->auth_type = auth_type;
-  memcpy(auth->descriptor_cookie, descriptor_cookie_tmp,
-         REND_DESC_COOKIE_LEN);
-  /* Add parsed client authorization to local map. */
-  if (!auth_hid_servs)
-    auth_hid_servs = strmap_new();
-  strmap_set(auth_hid_servs, auth->onion_address, auth);
-  auth = NULL;
-  res = 1;
- free:
-  if (sl)
-    SMARTLIST_FOREACH(sl, char *, c, tor_free(c););
-  smartlist_free(sl);
-  if (auth)
-    rend_service_authorization_free(auth);
   return res;
 }
 

Modified: tor/branches/121-hs-authorization/src/or/rendservice.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/rendservice.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/rendservice.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -45,6 +45,13 @@
   /* Fields specified in config file */
   char *directory; /**< where in the filesystem it stores it */
   smartlist_t *ports; /**< List of rend_service_port_config_t */
+  int descriptor_version; /**< Rendezvous descriptor version that will be
+                           * published. */
+  rend_auth_type_t auth_type; /**< Client authorization type or 0 if no client
+                               * authorization is performed. */
+  smartlist_t *clients; /**< List of rend_authorized_client_t's of
+                         * clients that may access our service. Can be NULL
+                         * if no client authorization is peformed. */
   /* Other fields */
   crypto_pk_env_t *private_key; /**< Permanent hidden-service key. */
   char service_id[REND_SERVICE_ID_LEN_BASE32+1]; /**< Onion address without
@@ -62,12 +69,6 @@
                          * up-to-date. */
   time_t next_upload_time; /**< Scheduled next hidden service descriptor
                             * upload time. */
-  int descriptor_version; /**< Rendezvous descriptor version that will be
-                           * published. */
-  int auth_type; /**< Client authorization type or 0 if no client
-                  * authorization is performed. */
-  smartlist_t *clients; /**< List of rend_authorized_client_t's for
-                         * clients that may access our service. */
   smartlist_t *accepted_intros; /**< List of client_access_event_t's for
                                  * accepted and answered INTRODUCE2 cells. */
 } rend_service_t;
@@ -93,16 +94,22 @@
 
 /** Helper: free storage held by a single service authorized client entry. */
 static void
-rend_authorized_client_free(void *authorized_client)
+rend_authorized_client_free(rend_authorized_client_t *client)
 {
-  rend_authorized_client_t *client = authorized_client;
-  if (!authorized_client) return;
+  if (!client) return;
   if (client->client_key)
     crypto_free_pk_env(client->client_key);
   tor_free(client->client_name);
   tor_free(client);
 }
 
+/** Helper for strmap_free. */
+static void
+rend_authorized_client_strmap_item_free(void *authorized_client)
+{
+  rend_authorized_client_free(authorized_client);
+}
+
 /** Release the storage held by <b>service</b>.
  */
 static void
@@ -155,10 +162,9 @@
 
   /* If the service is configured to publish unversioned (v0) and versioned
    * descriptors (v2 or higher), split it up into two separate services
-   * (unless it is configured to perform client authorization in which case
-   * ). */
+   * (unless it is configured to perform client authorization). */
   if (service->descriptor_version == -1) {
-    if (!service->auth_type) {
+    if (service->auth_type == REND_NO_AUTH) {
       rend_service_t *v0_service = tor_malloc_zero(sizeof(rend_service_t));
       v0_service->directory = tor_strdup(service->directory);
       v0_service->ports = smartlist_create();
@@ -170,23 +176,24 @@
       });
       v0_service->intro_period_started = service->intro_period_started;
       v0_service->descriptor_version = 0; /* Unversioned descriptor. */
-      v0_service->auth_type = 0;
+      v0_service->auth_type = REND_NO_AUTH;
       rend_add_service(v0_service);
     }
 
     service->descriptor_version = 2; /* Versioned descriptor. */
   }
 
-  if (service->auth_type && !service->descriptor_version) {
+  if (service->auth_type != REND_NO_AUTH && !service->descriptor_version) {
     log_warn(LD_CONFIG, "Hidden service with client authorization and "
                         "version 0 descriptors configured; ignoring.");
     rend_service_free(service);
     return;
   }
 
-  if (service->auth_type && smartlist_len(service->clients) == 0) {
+  if (service->auth_type != REND_NO_AUTH &&
+      smartlist_len(service->clients) == 0) {
     log_warn(LD_CONFIG, "Hidden service with client authorization but no "
-                         "clients; ignoring.");
+                        "clients; ignoring.");
     rend_service_free(service);
     return;
   }
@@ -320,19 +327,20 @@
       }
       smartlist_add(service->ports, portcfg);
     } else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
-      /* Parse comma-separated list of client names and add a
+      /* Parse auth type and comma-separated list of client names and add a
        * rend_authorized_client_t for each client to the service's list
        * of authorized clients. */
       smartlist_t *type_names_split, *clients;
-      if (service->auth_type) {
+      const char *authname;
+      int num_clients;
+      if (service->auth_type != REND_NO_AUTH) {
         log_warn(LD_CONFIG, "Got multiple HiddenServiceAuthorizeClient "
                  "lines for a single service.");
         rend_service_free(service);
         return -1;
       }
-      service->clients = smartlist_create();
       type_names_split = smartlist_create();
-      smartlist_split_string(type_names_split, line->value, " ", 0, 0);
+      smartlist_split_string(type_names_split, line->value, " ", 0, 2);
       if (smartlist_len(type_names_split) < 1) {
         log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This "
                          "should have been prevented when parsing the "
@@ -341,66 +349,54 @@
         rend_service_free(service);
         return -1;
       }
-      service->auth_type = (int) tor_parse_long(
-                 smartlist_get(type_names_split, 0), 10, 1, 2, NULL, NULL);
-      if (!service->auth_type) {
+      authname = smartlist_get(type_names_split, 0);
+      if (!strcasecmp(authname, "basic")) {
+        service->auth_type = REND_BASIC_AUTH;
+      } else if (!strcasecmp(authname, "stealth")) {
+        service->auth_type = REND_STEALTH_AUTH;
+      } else {
         log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
-                 "unrecognized auth-type '%s'. Only 1 or 2 are recognized.",
+                 "unrecognized auth-type '%s'. Only 'basic' or 'stealth' "
+                 "are recognized.",
                  (char *) smartlist_get(type_names_split, 0));
         SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
         smartlist_free(type_names_split);
         rend_service_free(service);
         return -1;
       }
+      service->clients = smartlist_create();
       if (smartlist_len(type_names_split) < 2) {
         log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
-                            "authorization type %d, but no client names.",
-                 service->auth_type);
+                            "auth-type '%s', but no client names.",
+                 service->auth_type == 1 ? "basic" : "stealth");
         SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
         smartlist_free(type_names_split);
         continue;
       }
-      if (smartlist_len(type_names_split) > 2) {
-        log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
-                            "illegal value '%s'. Must be formatted "
-                            "as 'HiddenServiceAuthorizeClient auth-type "
-                            "client-name,client-name,...' (without "
-                            "additional spaces in client-separated client "
-                            "list).",
-                 line->value);
-        SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
-        smartlist_free(type_names_split);
-        rend_service_free(service);
-        return -1;
-      }
       clients = smartlist_create();
       smartlist_split_string(clients, smartlist_get(type_names_split, 1),
-                             ",", 0, 0);
+                             ",", SPLIT_SKIP_SPACE, 0);
       SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
       smartlist_free(type_names_split);
-      if ((service->auth_type == 1 && smartlist_len(clients) > 512) ||
-          (service->auth_type == 2 && smartlist_len(clients) > 16)) {
-        log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d "
-                            "client authorization entries, but only a "
-                            "maximum of %d entries is allowed for "
-                            "authorization type %d.",
-                 smartlist_len(clients),
-                 service->auth_type == 1 ? 512 : 16,
-                 service->auth_type);
-        SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp));
-        smartlist_free(clients);
-        rend_service_free(service);
-        return -1;
+      /* Remove duplicate client names. */
+      num_clients = smartlist_len(clients);
+      smartlist_sort_strings(clients);
+      smartlist_uniq_strings(clients);
+      if (smartlist_len(clients) < num_clients) {
+        log_info(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d "
+                            "duplicate client name(s); removing.",
+                 num_clients - smartlist_len(clients));
+        num_clients = smartlist_len(clients);
       }
-      SMARTLIST_FOREACH(clients, char *, client_name, {
+      SMARTLIST_FOREACH_BEGIN(clients, const char *, client_name)
+      {
         rend_authorized_client_t *client;
         size_t len = strlen(client_name);
-        int found_duplicate = 0;
-        if (len < 1 || len > 19) {
+        if (len < 1 || len > REND_CLIENTNAME_MAX_LEN) {
           log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains an "
                               "illegal client name: '%s'. Length must be "
-                              "between 1 and 19 characters.",
-                   client_name);
+                              "between 1 and %d characters.",
+                   client_name, REND_CLIENTNAME_MAX_LEN);
           SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp));
           smartlist_free(clients);
           rend_service_free(service);
@@ -416,24 +412,29 @@
           rend_service_free(service);
           return -1;
         }
-        /* Check if client name is duplicate. */
-        SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *, c, {
-          if (!strcmp(c->client_name, client_name)) {
-            log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains a "
-                     "duplicate client name: '%s'; ignoring.", client_name);
-            found_duplicate = 1;
-            break;
-          }
-        });
-        if (found_duplicate)
-          continue;
         client = tor_malloc_zero(sizeof(rend_authorized_client_t));
-        client->client_name = strdup(client_name);
+        client->client_name = tor_strdup(client_name);
         smartlist_add(service->clients, client);
         log_debug(LD_REND, "Adding client name '%s'", client_name);
-      });
+      }
+      SMARTLIST_FOREACH_END(client_name);
       SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp));
       smartlist_free(clients);
+      /* Ensure maximum number of clients. */
+      if ((service->auth_type == REND_BASIC_AUTH &&
+            smartlist_len(service->clients) > 512) ||
+          (service->auth_type == REND_STEALTH_AUTH &&
+            smartlist_len(service->clients) > 16)) {
+        log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d "
+                            "client authorization entries, but only a "
+                            "maximum of %d entries is allowed for "
+                            "authorization type '%s'.",
+                 smartlist_len(service->clients),
+                 service->auth_type == REND_BASIC_AUTH ? 512 : 16,
+                 service->auth_type == 1 ? "basic" : "stealth");
+        rend_service_free(service);
+        return -1;
+      }
     } else {
       smartlist_t *versions;
       char *version_str;
@@ -522,13 +523,11 @@
 int
 rend_service_load_keys(void)
 {
-  int i;
-  rend_service_t *s;
+  int r = 0;
   char fname[512];
   char buf[1500];
 
-  for (i=0; i < smartlist_len(rend_service_list); ++i) {
-    s = smartlist_get(rend_service_list,i);
+  SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, s) {
     if (s->private_key)
       continue;
     log_info(LD_REND, "Loading hidden-service keys from \"%s\"",
@@ -573,26 +572,26 @@
     }
 
     /* If client authorization is configured, load or generate keys. */
-    if (s->auth_type) {
-      char *client_keys_str;
+    if (s->auth_type != REND_NO_AUTH) {
+      char *client_keys_str = NULL;
       strmap_t *parsed_clients = strmap_new();
       char cfname[512];
+      FILE *cfile, *hfile;
+      open_file_t *open_cfile = NULL, *open_hfile = NULL;
 
       /* Load client keys and descriptor cookies, if available. */
-      if (strlcpy(cfname,s->directory,sizeof(cfname)) >= sizeof(cfname) ||
-          strlcat(cfname,PATH_SEPARATOR"client_keys",sizeof(cfname))
-                                                      >= sizeof(cfname)) {
+      if (tor_snprintf(cfname, sizeof(cfname), "%s"PATH_SEPARATOR"client_keys",
+                       s->directory)<0) {
         log_warn(LD_CONFIG, "Directory name too long to store client keys "
                  "file: \"%s\".", s->directory);
-        return -1;
+        goto err;
       }
       client_keys_str = read_file_to_str(cfname, RFTS_IGNORE_MISSING, NULL);
       if (client_keys_str) {
         if (rend_parse_client_keys(parsed_clients, client_keys_str) < 0) {
           log_warn(LD_CONFIG, "Previously stored client_keys file could not "
                               "be parsed.");
-          tor_free(client_keys_str);
-          return -1;
+          goto err;
         } else {
           log_info(LD_CONFIG, "Parsed %d previously stored client entries.",
                    strmap_size(parsed_clients));
@@ -601,18 +600,22 @@
       }
 
       /* Prepare client_keys and hostname files. */
-      if (write_str_to_file(cfname, "", 0) < 0) {
-        log_warn(LD_CONFIG, "Could not clear client_keys file.");
-        return -1;
+      if (!(cfile = start_writing_to_stdio_file(cfname, OPEN_FLAGS_REPLACE,
+                                                0600, &open_cfile))) {
+        log_warn(LD_CONFIG, "Could not open client_keys file %s",
+                 escaped(cfname));
+        goto err;
       }
-      if (write_str_to_file(fname, "", 0) < 0) {
-        log_warn(LD_CONFIG, "Could not clear hostname file.");
-        return -1;
+      if (!(hfile = start_writing_to_stdio_file(fname, OPEN_FLAGS_REPLACE,
+                                                0600, &open_hfile))) {
+        log_warn(LD_CONFIG, "Could not open hostname file %s", escaped(fname));
+        goto err;
       }
 
       /* Either use loaded keys for configured clients or generate new
        * ones if a client is new. */
-      SMARTLIST_FOREACH(s->clients, rend_authorized_client_t *, client, {
+      SMARTLIST_FOREACH_BEGIN(s->clients, rend_authorized_client_t *, client)
+      {
         char desc_cook_out[3*REND_DESC_COOKIE_LEN_BASE64+1];
         char service_id[16+1];
         rend_authorized_client_t *parsed =
@@ -630,30 +633,27 @@
                           client->descriptor_cookie,
                           REND_DESC_COOKIE_LEN) < 0) {
           log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
-          strmap_free(parsed_clients, rend_authorized_client_free);
+          strmap_free(parsed_clients, rend_authorized_client_strmap_item_free);
           return -1;
         }
         /* Copy client key from parsed entry or create new one if required. */
         if (parsed && parsed->client_key) {
           client->client_key = crypto_pk_dup_key(parsed->client_key);
-        } else if (s->auth_type == 2) {
+        } else if (s->auth_type == REND_STEALTH_AUTH) {
           /* Create private key for client. */
           crypto_pk_env_t *prkey = NULL;
           if (!(prkey = crypto_new_pk_env())) {
             log_warn(LD_BUG,"Error constructing client key");
-            strmap_free(parsed_clients, rend_authorized_client_free);
-            return -1;
+            goto err;
           }
           if (crypto_pk_generate_key(prkey)) {
             log_warn(LD_BUG,"Error generating client key");
-            strmap_free(parsed_clients, rend_authorized_client_free);
-            return -1;
+            goto err;
           }
           if (crypto_pk_check_key(prkey) <= 0) {
             log_warn(LD_BUG,"Generated client key seems invalid");
             crypto_free_pk_env(prkey);
-            strmap_free(parsed_clients, rend_authorized_client_free);
-            return -1;
+            goto err;
           }
           client->client_key = prkey;
         }
@@ -664,8 +664,7 @@
                                client->client_name, desc_cook_out);
         if (written < 0) {
           log_warn(LD_BUG, "Could not write client entry.");
-          strmap_free(parsed_clients, rend_authorized_client_free);
-          return -1;
+          goto err;
         }
         if (client->client_key) {
           char *client_key_out;
@@ -673,47 +672,71 @@
                                                 &client_key_out, &len);
           if (rend_get_service_id(client->client_key, service_id)<0) {
             log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
-            strmap_free(parsed_clients, rend_authorized_client_free);
-            return -1;
+            goto err;
           }
           written = tor_snprintf(buf + written, sizeof(buf) - written,
-                                 "client-key\n%s",
-                                 client_key_out);
+                                 "client-key\n%s", client_key_out);
           if (written < 0) {
             log_warn(LD_BUG, "Could not write client entry.");
-            strmap_free(parsed_clients, rend_authorized_client_free);
-            return -1;
+            goto err;
           }
         }
-        append_bytes_to_file(cfname, buf, strlen(buf), 0);
+
+        if (fputs(buf, cfile) < 0) {
+          log_warn(LD_FS, "Could not append client entry to file: %s",
+                   strerror(errno));
+          goto err;
+        }
+
         /* Add line to hostname file. */
-        if (s->auth_type == 2) {
+        if (s->auth_type == REND_BASIC_AUTH) {
+          /* Remove == signs (newline has been removed above). */
+          desc_cook_out[strlen(desc_cook_out)-2] = '\0';
+          tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n",
+                       s->service_id, desc_cook_out, client->client_name);
+        } else {
           char extended_desc_cookie[REND_DESC_COOKIE_LEN+1];
           memcpy(extended_desc_cookie, client->descriptor_cookie,
                  REND_DESC_COOKIE_LEN);
-          extended_desc_cookie[REND_DESC_COOKIE_LEN] = (s->auth_type - 1) << 4;
+          extended_desc_cookie[REND_DESC_COOKIE_LEN] =
+              ((int)s->auth_type - 1) << 4;
           if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
                             extended_desc_cookie,
                             REND_DESC_COOKIE_LEN+1) < 0) {
             log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
-            strmap_free(parsed_clients, rend_authorized_client_free);
-            return -1;
+            goto err;
           }
           desc_cook_out[strlen(desc_cook_out)-3] = '\0'; /* Remove A= and
                                                             newline. */
           tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n",
                        service_id, desc_cook_out, client->client_name);
-        } else {
-          /* Remove == signs (newline has been removed above). */
-          desc_cook_out[strlen(desc_cook_out)-2] = '\0';
-          tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n",
-                       s->service_id, desc_cook_out, client->client_name);
         }
-        append_bytes_to_file(fname, buf, strlen(buf), 0);
-      });
+
+        if (fputs(buf, hfile)<0) {
+          log_warn(LD_FS, "Could not append host entry to file: %s",
+                   strerror(errno));
+          goto err;
+        }
+      }
+      SMARTLIST_FOREACH_END(client);
+
+      goto done;
+    err:
+      r = -1;
+    done:
+      tor_free(client_keys_str);
+      strmap_free(parsed_clients, rend_authorized_client_strmap_item_free);
+      if (r<0) {
+        abort_writing_to_file(open_cfile);
+        abort_writing_to_file(open_hfile);
+        return r;
+      } else {
+        finish_writing_to_file(open_cfile);
+        finish_writing_to_file(open_hfile);
+      }
     }
-  }
-  return 0;
+  } SMARTLIST_FOREACH_END(s);
+  return r;
 }
 
 /** Return the service whose public key has a digest of <b>digest</b> and

Modified: tor/branches/121-hs-authorization/src/or/routerlist.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/routerlist.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/routerlist.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -1431,6 +1431,19 @@
  * routers by bandwidth. */
 #define DEFAULT_MAX_BELIEVABLE_BANDWIDTH 10000000 /* 10 MB/sec */
 
+/** Return the smaller of the router's configured BandwidthRate
+ * and its advertised capacity, capped by max-believe-bw. */
+uint32_t
+router_get_advertised_bandwidth_capped(routerinfo_t *router)
+{
+  uint32_t result = router->bandwidthcapacity;
+  if (result > router->bandwidthrate)
+    result = router->bandwidthrate;
+  if (result > DEFAULT_MAX_BELIEVABLE_BANDWIDTH)
+    result = DEFAULT_MAX_BELIEVABLE_BANDWIDTH;
+  return result;
+}
+
 /** Eventually, the number we return will come from the directory
  * consensus, so clients can dynamically update to better numbers.
  *

Modified: tor/branches/121-hs-authorization/src/or/routerparse.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/routerparse.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/routerparse.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -53,9 +53,11 @@
   K_DIR_OPTIONS,
   K_CLIENT_VERSIONS,
   K_SERVER_VERSIONS,
+  K_P,
   K_R,
   K_S,
   K_V,
+  K_W,
   K_EVENTDNS,
   K_EXTRA_INFO,
   K_EXTRA_INFO_DIGEST,
@@ -145,6 +147,7 @@
 typedef enum {
   NO_OBJ,        /**< No object, ever. */
   NEED_OBJ,      /**< Object is required. */
+  NEED_SKEY_1024,/**< Object is required, and must be a 1024 bit private key */
   NEED_KEY_1024, /**< Object is required, and must be a 1024 bit public key */
   NEED_KEY,      /**< Object is required, and must be a public key. */
   OBJ_OK,        /**< Object is optional. */
@@ -263,9 +266,11 @@
 /** List of tokens allowable in the body part of v2 and v3 networkstatus
  * documents. */
 static token_rule_t rtrstatus_token_table[] = {
+  T01("p",                   K_P,               CONCAT_ARGS, NO_OBJ ),
   T1( "r",                   K_R,                   GE(8),   NO_OBJ ),
   T1( "s",                   K_S,                   ARGS,    NO_OBJ ),
   T01("v",                   K_V,               CONCAT_ARGS, NO_OBJ ),
+  T01("w",                   K_W,                   ARGS,    NO_OBJ ),
   T0N("opt",                 K_OPT,             CONCAT_ARGS, OBJ_OK ),
   END_OF_TABLE
 };
@@ -361,7 +366,7 @@
 static token_rule_t client_keys_token_table[] = {
   T1_START("client-name", C_CLIENT_NAME, CONCAT_ARGS, NO_OBJ),
   T1("descriptor-cookie", C_DESCRIPTOR_COOKIE, EQ(1), NO_OBJ),
-  T01("client-key", C_CLIENT_KEY, NO_ARGS, NEED_KEY_1024),
+  T01("client-key", C_CLIENT_KEY, NO_ARGS, NEED_SKEY_1024),
   END_OF_TABLE
 };
 
@@ -1862,6 +1867,40 @@
     }
   }
 
+  /* handle weighting/bandwidth info */
+  if ((tok = find_first_by_keyword(tokens, K_W))) {
+    int i;
+    for (i=0; i < tok->n_args; ++i) {
+      if (!strcmpstart(tok->args[i], "Bandwidth=")) {
+        int ok;
+        rs->bandwidth = tor_parse_ulong(strchr(tok->args[i], '=')+1, 10,
+                                        0, UINT32_MAX, &ok, NULL);
+        if (!ok) {
+          log_warn(LD_DIR, "Invalid Bandwidth %s", escaped(tok->args[i]));
+          goto err;
+        }
+        rs->has_bandwidth = 1;
+      }
+    }
+  }
+
+  /* parse exit policy summaries */
+  if ((tok = find_first_by_keyword(tokens, K_P))) {
+    tor_assert(tok->n_args == 1);
+    if (strcmpstart(tok->args[0], "accept ") &&
+        strcmpstart(tok->args[0], "reject ")) {
+      log_err(LD_DIR, "Unknown exit policy summary type %s.",
+               escaped(tok->args[0]));
+      goto err;
+    }
+    /* XXX weasel: parse this into ports and represent them somehow smart,
+     * maybe not here but somewhere on if we need it for the client.
+     * we should still parse it here to check it's valid tho.
+     */
+    rs->exitsummary = tor_strdup(tok->args[0]);
+    rs->has_exitsummary = 1;
+  }
+
   if (!strcasecmp(rs->nickname, UNNAMED_ROUTER_NICKNAME))
     rs->is_named = 0;
 
@@ -2092,6 +2131,7 @@
   struct in_addr in;
   int i, inorder, n_signatures = 0;
   memarea_t *area = NULL, *rs_area = NULL;
+  tor_assert(s);
 
   if (router_get_networkstatus_v3_hash(s, ns_digest)) {
     log_warn(LD_DIR, "Unable to compute digest of network-status");
@@ -2802,6 +2842,7 @@
       }
       break;
     case NEED_KEY_1024:
+    case NEED_SKEY_1024:
       if (tok->key && crypto_pk_keysize(tok->key) != PK_BYTES) {
         tor_snprintf(ebuf, sizeof(ebuf), "Wrong size on key for %s: %d bits",
                      kwd, (int)crypto_pk_keysize(tok->key));
@@ -2812,6 +2853,19 @@
       if (!tok->key) {
         tor_snprintf(ebuf, sizeof(ebuf), "Missing public key for %s", kwd);
       }
+      if (o_syn != NEED_SKEY_1024) {
+        if (crypto_pk_key_is_private(tok->key)) {
+          tor_snprintf(ebuf, sizeof(ebuf),
+               "Private key given for %s, which wants a public key", kwd);
+          RET_ERR(ebuf);
+        }
+      } else { /* o_syn == NEED_SKEY_1024 */
+        if (!crypto_pk_key_is_private(tok->key)) {
+          tor_snprintf(ebuf, sizeof(ebuf),
+               "Public key given for %s, which wants a private key", kwd);
+          RET_ERR(ebuf);
+        }
+      }
       break;
     case OBJ_OK:
       break;
@@ -3849,7 +3903,7 @@
       goto err;
     }
     parsed_entry = tor_malloc_zero(sizeof(rend_authorized_client_t));
-    parsed_entry->client_name = strdup(tok->args[0]);
+    parsed_entry->client_name = tor_strdup(tok->args[0]);
     strmap_set(parsed_clients, parsed_entry->client_name, parsed_entry);
     /* Parse client key. */
     tok = find_first_by_keyword(tokens, C_CLIENT_KEY);
@@ -3864,11 +3918,11 @@
     tor_assert(tok->n_args == 1);
     if (strlen(tok->args[0]) != REND_DESC_COOKIE_LEN_BASE64 + 2) {
       log_warn(LD_REND, "Descriptor cookie has illegal length: %s",
-               tok->args[0]);
+               escaped(tok->args[0]));
       goto err;
     }
-    /* The size of descriptor_cookie_tmp needs to REND_DESC_COOKIE_LEN+2,
-     * because a base64 of length 24 does not fit into 16 bytes in all
+    /* The size of descriptor_cookie_tmp needs to be REND_DESC_COOKIE_LEN+2,
+     * because a base64 encoding of length 24 does not fit into 16 bytes in all
      * cases. */
     if ((base64_decode(descriptor_cookie_tmp, REND_DESC_COOKIE_LEN+2,
                        tok->args[0], REND_DESC_COOKIE_LEN_BASE64+2+1)

Modified: tor/branches/121-hs-authorization/src/or/test.c
===================================================================
--- tor/branches/121-hs-authorization/src/or/test.c	2008-08-15 21:58:51 UTC (rev 16565)
+++ tor/branches/121-hs-authorization/src/or/test.c	2008-08-16 11:36:09 UTC (rev 16566)
@@ -1328,7 +1328,7 @@
   sin->sin_family = AF_INET;
   sin->sin_port = 9090;
   sin->sin_addr.s_addr = htonl(0x7f7f0102); /*127.127.1.2*/
-  tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin);
+  tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin, NULL);
   test_eq(tor_addr_family(&t1), AF_INET);
   test_eq(tor_addr_to_ipv4h(&t1), 0x7f7f0102);
 
@@ -1344,7 +1344,7 @@
   sin6->sin6_family = AF_INET6;
   sin6->sin6_port = htons(7070);
   sin6->sin6_addr.s6_addr[0] = 128;
-  tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin6);
+  tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin6, NULL);
   test_eq(tor_addr_family(&t1), AF_INET6);
   p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 0);
   test_streq(p1, "8000::");
@@ -2862,12 +2862,35 @@
 #undef median
 }
 
+static routerinfo_t *
+generate_ri_from_rs(const vote_routerstatus_t *vrs)
+{
+  routerinfo_t *r;
+  const routerstatus_t *rs = &vrs->status;
+  static time_t published = 0;
+
+  r = tor_malloc_zero(sizeof(routerinfo_t));
+  memcpy(r->cache_info.identity_digest, rs->identity_digest, DIGEST_LEN);
+  memcpy(r->cache_info.signed_descriptor_digest, rs->descriptor_digest,
+         DIGEST_LEN);
+  r->cache_info.do_not_cache = 1;
+  r->cache_info.routerlist_index = -1;
+  r->cache_info.signed_descriptor_body =
+    tor_strdup("123456789012345678901234567890123");
+  r->cache_info.signed_descriptor_len =
+    strlen(r->cache_info.signed_descriptor_body);
+  r->exit_policy = smartlist_create();
+  r->cache_info.published_on = ++published + time(NULL);
+  return r;
+};
+
 static void
 test_v3_networkstatus(void)
 {
   authority_cert_t *cert1, *cert2, *cert3;
   crypto_pk_env_t *sign_skey_1, *sign_skey_2, *sign_skey_3;
   crypto_pk_env_t *sign_skey_leg1;
+  const char *msg=NULL;
 
   time_t now = time(NULL);
   networkstatus_voter_info_t *voter;
@@ -2944,6 +2967,8 @@
   /* all flags but running cleared */
   rs->is_running = 1;
   smartlist_add(vote->routerstatus_list, vrs);
+  test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
+
   /* add the second routerstatus. */
   vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
   rs = &vrs->status;
@@ -2958,6 +2983,8 @@
   rs->is_exit = rs->is_stable = rs->is_fast = rs->is_running =
     rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1;
   smartlist_add(vote->routerstatus_list, vrs);
+  test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
+
   /* add the third routerstatus. */
   vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
   rs = &vrs->status;
@@ -2965,13 +2992,15 @@
   rs->published_on = now-1000;
   strlcpy(rs->nickname, "router3", sizeof(rs->nickname));
   memset(rs->identity_digest, 33, DIGEST_LEN);
-  memset(rs->descriptor_digest, 78, DIGEST_LEN);
+  memset(rs->descriptor_digest, 79, DIGEST_LEN);
   rs->addr = 0xAA009901;
   rs->or_port = 400;
   rs->dir_port = 9999;
   rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
     rs->is_running = rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1;
   smartlist_add(vote->routerstatus_list, vrs);
+  test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
+
   /* add a fourth routerstatus that is not running. */
   vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
   rs = &vrs->status;
@@ -2985,6 +3014,7 @@
   rs->dir_port = 1999;
   /* Running flag (and others) cleared */
   smartlist_add(vote->routerstatus_list, vrs);
+  test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
 
   /* dump the vote and try to parse it. */
   v1_text = format_networkstatus_vote(sign_skey_1, vote);
@@ -3108,6 +3138,7 @@
   tor_free(vrs);
   vrs = smartlist_get(vote->routerstatus_list, 0);
   memset(vrs->status.descriptor_digest, (int)'Z', DIGEST_LEN);
+  test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
 
   v3_text = format_networkstatus_vote(sign_skey_3, vote);
   test_assert(v3_text);
@@ -3327,12 +3358,37 @@
 }
 
 static void
+test_policy_summary_helper(const char *policy_str,
+                           const char *expected_summary)
+{
+  config_line_t line;
+  smartlist_t *policy;
+  char *summary;
+
+  policy = NULL;
+  line.key = (char*)"foo";
+  line.value = (char *)policy_str;
+  line.next = NULL;
+
+  test_assert(0 == policies_parse_exit_policy(&line, &policy, 0, NULL));
+  summary = policy_summarize(policy);
+
+  test_assert(summary != NULL);
+  test_streq(summary, expected_summary);
+  tor_free(summary);
+  addr_policy_list_free(policy);
+}
+
+static void
 test_policies(void)
 {
+  int i;
   smartlist_t *policy, *policy2;
   addr_policy_t *p;
   tor_addr_t tar;
   config_line_t line;
+  smartlist_t *sm;
+  char *policy_str;
 
   policy = smartlist_create();
 
@@ -3386,6 +3442,92 @@
   test_eq(smartlist_len(policy), 2);
 
   addr_policy_list_free(policy);
+
+  /* test policy summaries */
+  /* check if we properly ignore private IP addresses */
+  test_policy_summary_helper("reject 192.168.0.0/16:*,"
+                             "reject 0.0.0.0/8:*,"
+                             "reject 10.0.0.0/8:*,"
+                             "accept *:10-30,"
+                             "accept *:90,"
+                             "reject *:*",
+                             "accept 10-30,90");
+  /* check all accept policies, and proper counting of rejects */
+  test_policy_summary_helper("reject 11.0.0.0/9:80,"
+                             "reject 12.0.0.0/9:80,"
+                             "reject 13.0.0.0/9:80,"
+                             "reject 14.0.0.0/9:80,"
+                             "accept *:*", "accept 1-65535");
+  test_policy_summary_helper("reject 11.0.0.0/9:80,"
+                             "reject 12.0.0.0/9:80,"
+                             "reject 13.0.0.0/9:80,"
+                             "reject 14.0.0.0/9:80,"
+                             "reject 15.0.0.0:81,"
+                             "accept *:*", "accept 1-65535");
+  test_policy_summary_helper("reject 11.0.0.0/9:80,"
+                             "reject 12.0.0.0/9:80,"
+                             "reject 13.0.0.0/9:80,"
+                             "reject 14.0.0.0/9:80,"
+                             "reject 15.0.0.0:80,"
+                             "accept *:*",
+                             "reject 80");
+  /* no exits */
+  test_policy_summary_helper("accept 11.0.0.0/9:80,"
+                             "reject *:*",
+                             "reject 1-65535");
+  /* port merging */
+  test_policy_summary_helper("accept *:80,"
+                             "accept *:81,"
+                             "accept *:100-110,"
+                             "accept *:111,"
+                             "reject *:*",
+                             "accept 80-81,100-111");
+  /* border ports */
+  test_policy_summary_helper("accept *:1,"
+                             "accept *:3,"
+                             "accept *:65535,"
+                             "reject *:*",
+                             "accept 1,3,65535");
+  /* holes */
+  test_policy_summary_helper("accept *:1,"
+                             "accept *:3,"
+                             "accept *:5,"
+                             "accept *:7,"
+                             "reject *:*",
+                             "accept 1,3,5,7");
+  test_policy_summary_helper("reject *:1,"
+                             "reject *:3,"
+                             "reject *:5,"
+                             "reject *:7,"
+                             "accept *:*",
+                             "reject 1,3,5,7");
+  /* truncation ports */
+  sm = smartlist_create();
+  for (i=1; i<2000; i+=2) {
+    char buf[POLICY_BUF_LEN];
+    tor_snprintf(buf, sizeof(buf), "reject *:%d", i);
+    smartlist_add(sm, tor_strdup(buf));
+  }
+  smartlist_add(sm, tor_strdup("accept *:*"));
+  policy_str = smartlist_join_strings(sm, ",", 0, NULL);
+  test_policy_summary_helper( policy_str,
+    "accept 2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,"
+    "46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,"
+    "92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,"
+    "130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,"
+    "166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,"
+    "202,204,206,208,210,212,214,216,218,220,222,224,226,228,230,232,234,236,"
+    "238,240,242,244,246,248,250,252,254,256,258,260,262,264,266,268,270,272,"
+    "274,276,278,280,282,284,286,288,290,292,294,296,298,300,302,304,306,308,"
+    "310,312,314,316,318,320,322,324,326,328,330,332,334,336,338,340,342,344,"
+    "346,348,350,352,354,356,358,360,362,364,366,368,370,372,374,376,378,380,"
+    "382,384,386,388,390,392,394,396,398,400,402,404,406,408,410,412,414,416,"
+    "418,420,422,424,426,428,430,432,434,436,438,440,442,444,446,448,450,452,"
+    "454,456,458,460,462,464,466,468,470,472,474,476,478,480,482,484,486,488,"
+    "490,492,494,496,498,500,502,504,506,508,510,512,514,516,518,520,522");
+  tor_free(policy_str);
+  SMARTLIST_FOREACH(sm, char *, s, tor_free(s));
+  smartlist_clear(sm);
 }
 
 static void