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

Tor Ipv6-Patch



Dear list,

I wrote an extensive patch to make tor support
* hidden services on ipv6-addresses
* binding for socks on an ipv6-address
* convert a lot of the internal data-structures for the comming full
ipv6-support.

I wrote it to tor-volunteers@xxxxxxxxxxxxxx on friday but got no
answer thus I am posting them here.

The code may not use the prefered naming-conventions or the
tor_address_t -data-structure (because that one uses socket_addr
and we only need the actual address-part) but I guess a refactoring
for it to meet the coding-guidelines is far less work then writing it
and getting it to work in the first place.

Marcus Wolschon
Index: or/circuitlist.c
===================================================================
--- or/circuitlist.c	(Revision 12621)
+++ or/circuitlist.c	(Arbeitskopie)
@@ -216,6 +216,11 @@
     global_circuitlist = circ;
   }
 }
+/** compare the 2 given ipv6-addresses
+ *
+ *  * returns 0 if they are equal.
+ *   */
+int compare_in6_addr(struct in6_addr a, struct in6_addr b);
 
 /** Append to <b>out</b> all circuits in state OR_WAIT waiting for
  * the given connection. */
@@ -235,7 +240,9 @@
     tor_assert(circ->state == CIRCUIT_STATE_OR_WAIT);
     if (tor_digest_is_zero(circ->n_conn_id_digest)) {
       /* Look at addr/port. This is an unkeyed connection. */
-      if (circ->n_addr != or_conn->_base.addr ||
+      if (circ->n_family != or_conn->_base.socket_family ||
+          (circ->n_family == AF_INET && circ->n_addr4 != or_conn->_base.addr4) ||
+          (circ->n_family == AF_INET6 && compare_in6_addr(circ->n_addr6, or_conn->_base.addr6) != 0) ||
           circ->n_port != or_conn->_base.port)
         continue;
     } else {
@@ -558,8 +565,10 @@
         }
       }
     }
-    if (!circ->n_conn && circ->n_addr && circ->n_port &&
-        circ->n_addr == conn->addr &&
+    if (!circ->n_conn && (circ->n_family != AF_INET || circ->n_addr4) && circ->n_port &&
+        circ->n_family == conn->socket_family &&
+        (circ->n_family != AF_INET || circ->n_addr4 == conn->addr4)&&
+        (circ->n_family != AF_INET6 || compare_in6_addr(circ->n_addr6, conn->addr6) == 0)&&
         circ->n_port == conn->port &&
         conn->type == CONN_TYPE_OR &&
         !memcmp(TO_OR_CONN(conn)->identity_digest, circ->n_conn_id_digest,
Index: or/connection_or.c
===================================================================
--- or/connection_or.c	(Revision 12621)
+++ or/connection_or.c	(Arbeitskopie)
@@ -336,14 +336,20 @@
 
   if (get_options()->HttpsProxy) {
     char buf[1024];
-    char addrbuf[INET_NTOA_BUF_LEN];
-    struct in_addr in;
+    char addrbuf[64];
     char *base64_authenticator=NULL;
     const char *authenticator = get_options()->HttpsProxyAuthenticator;
+    
+    if (conn->socket_family == AF_INET6) {
+      struct in6_addr addr;
+      addr = conn->addr6;
+      tor_inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf));
+    } else {
+      struct in_addr in;
+      in.s_addr = htonl(conn->addr4);
+      tor_inet_ntoa(&in, addrbuf, sizeof(addrbuf));
+    }
 
-    in.s_addr = htonl(conn->addr);
-    tor_inet_ntoa(&in, addrbuf, sizeof(addrbuf));
-
     if (authenticator) {
       base64_authenticator = alloc_http_authenticator(authenticator);
       if (!base64_authenticator)
@@ -375,6 +381,58 @@
  * have an addr/port/id_digest, then fill in as much as we can. Start
  * by checking to see if this describes a router we know. */
 static void
+connection_or_init_conn_from_address6(or_connection_t *conn,
+                                     struct in6_addr addr6, uint16_t port,
+                                     const char *id_digest,
+                                     int started_here)
+{
+  or_options_t *options = get_options();
+  routerinfo_t *r = router_get_by_digest(id_digest);
+  conn->bandwidthrate = (int)options->BandwidthRate;
+  conn->read_bucket = conn->bandwidthburst = (int)options->BandwidthBurst;
+  connection_or_set_identity_digest(conn, id_digest);
+  conn->_base.socket_family = AF_INET6;
+  conn->_base.addr6 = addr6;
+  conn->_base.port = port;
+  conn->real_socket_family = AF_INET6;
+  conn->real_addr6 = addr6;
+  if (r) {
+    if (conn->_base.addr4 == r->addr)
+      conn->is_canonical = 1;
+    if (!started_here) {
+      /* Override the addr/port, so our log messages will make sense.
+       * This is dangerous, since if we ever try looking up a conn by
+       * its actual addr/port, we won't remember. Careful! */
+      /* XXXX020 this is stupid, and it's the reason we need real_addr to
+       * track is_canonical properly. */
+      conn->_base.socket_family = AF_INET;
+      conn->_base.addr4 = r->addr;
+      conn->_base.port = r->or_port;
+    }
+    conn->nickname = tor_strdup(r->nickname);
+    tor_free(conn->_base.address);
+    conn->_base.address = tor_strdup(r->address);
+  } else {
+    const char *n;
+    /* If we're an authoritative directory server, we may know a
+     * nickname for this router. */
+    n = dirserv_get_nickname_by_digest(id_digest);
+    if (n) {
+      conn->nickname = tor_strdup(n);
+    } else {
+      conn->nickname = tor_malloc(HEX_DIGEST_LEN+2);
+      conn->nickname[0] = '$';
+      base16_encode(conn->nickname+1, HEX_DIGEST_LEN+1,
+                    conn->identity_digest, DIGEST_LEN);
+    }
+    tor_free(conn->_base.address);
+    conn->_base.address = tor_dup_addr6(addr6);
+  }
+}
+/** If we don't necessarily know the router we're connecting to, but we
+ * have an addr/port/id_digest, then fill in as much as we can. Start
+ * by checking to see if this describes a router we know. */
+static void
 connection_or_init_conn_from_address(or_connection_t *conn,
                                      uint32_t addr, uint16_t port,
                                      const char *id_digest,
@@ -385,11 +443,13 @@
   conn->bandwidthrate = (int)options->BandwidthRate;
   conn->read_bucket = conn->bandwidthburst = (int)options->BandwidthBurst;
   connection_or_set_identity_digest(conn, id_digest);
-  conn->_base.addr = addr;
+  conn->_base.socket_family = AF_INET;
+  conn->_base.addr4 = addr;
   conn->_base.port = port;
-  conn->real_addr = addr;
+  conn->real_socket_family = AF_INET;
+  conn->real_addr4 = addr;
   if (r) {
-    if (conn->_base.addr == r->addr)
+    if (conn->_base.addr4 == r->addr)
       conn->is_canonical = 1;
     if (!started_here) {
       /* Override the addr/port, so our log messages will make sense.
@@ -397,7 +457,8 @@
        * its actual addr/port, we won't remember. Careful! */
       /* XXXX020 this is stupid, and it's the reason we need real_addr to
        * track is_canonical properly. */
-      conn->_base.addr = r->addr;
+      conn->_base.socket_family = AF_INET;
+      conn->_base.addr4 = r->addr;
       conn->_base.port = r->or_port;
     }
     conn->nickname = tor_strdup(r->nickname);
@@ -775,8 +836,13 @@
                                   conn->handshake_state->started_here,
                                               id_digest) < 0)
     return -1;
-  connection_or_init_conn_from_address(conn, conn->_base.addr,
-                                       conn->_base.port, id_digest, 0);
+  if (conn->_base.socket_family == AF_INET6) {
+    connection_or_init_conn_from_address6(conn, conn->_base.addr6,
+                                         conn->_base.port, id_digest, 0);
+  } else {
+    connection_or_init_conn_from_address(conn, conn->_base.addr4,
+                                         conn->_base.port, id_digest, 0);
+  }
   if (connection_or_act_on_netinfo(conn)<0)
     return -1;
   return connection_or_set_state_open(conn);
@@ -809,8 +875,13 @@
                                                 digest_rcvd) < 0)
       return -1;
     if (!started_here) {
-      connection_or_init_conn_from_address(conn,conn->_base.addr,
-                                           conn->_base.port, digest_rcvd, 0);
+      if (!conn->_base.socket_family == AF_INET6) {
+        connection_or_init_conn_from_address6(conn,conn->_base.addr6,
+                                             conn->_base.port, digest_rcvd, 0);
+      } else {
+        connection_or_init_conn_from_address(conn,conn->_base.addr4,
+                                             conn->_base.port, digest_rcvd, 0);
+      }
     }
     return connection_or_set_state_open(conn);
   } else {
@@ -1040,7 +1111,11 @@
   set_uint32(cell.payload, htonl(now));
   cell.payload[4] = RESOLVED_TYPE_IPV4;
   cell.payload[5] = 4;
-  set_uint32(cell.payload+6, htonl(conn->_base.addr));
+  if (conn->_base.socket_family == AF_INET6) {
+    log_warn(LD_BUG, "we do not support ipv6 in connection_or_send_netinfo() ");
+    return -1;
+  }
+  set_uint32(cell.payload+6, htonl(conn->_base.addr4));
 
   /* My address. */
   if ((me = router_get_my_routerinfo())) {
Index: or/rendservice.c
===================================================================
--- or/rendservice.c	(Revision 12621)
+++ or/rendservice.c	(Arbeitskopie)
@@ -20,7 +20,9 @@
 typedef struct rend_service_port_config_t {
   uint16_t virtual_port;
   uint16_t real_port;
-  uint32_t real_addr;
+  u_int16_t real_family;      // AF_INET or AF_INET6
+  uint32_t real_addr4;        // ipv4-address of hidden service depending on real_family
+  struct in6_addr real_addr6; // ipv6-address of hidden service depending on real_family
 } rend_service_port_config_t;
 
 /** Try to maintain this many intro points per service if possible. */
@@ -129,7 +131,6 @@
 {
   int i;
   rend_service_port_config_t *p;
-  struct in_addr addr;
 
   if (!service->intro_prefer_nodes)
     service->intro_prefer_nodes = tor_strdup("");
@@ -166,15 +167,25 @@
   } else {
     smartlist_set_capacity(service->ports, -1);
     smartlist_add(rend_service_list, service);
-    log_debug(LD_REND,"Configuring service with directory \"%s\"",
+    log_debug(LD_REND,"Configuring hidden service with directory \"%s\"",
               service->directory);
     for (i = 0; i < smartlist_len(service->ports); ++i) {
-      char addrbuf[INET_NTOA_BUF_LEN];
       p = smartlist_get(service->ports, i);
-      addr.s_addr = htonl(p->real_addr);
-      tor_inet_ntoa(&addr, addrbuf, sizeof(addrbuf));
-      log_debug(LD_REND,"Service maps port %d to %s:%d",
-                p->virtual_port, addrbuf, p->real_port);
+      if (p->real_family == AF_INET6) {
+         char addrbuf[42];
+         struct in6_addr addr;
+         addr = p->real_addr6;
+	 tor_inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf));
+         log_debug(LD_REND,"Hidden service maps ipv6 port %d to [%s]:%d",
+                   p->virtual_port, addrbuf, p->real_port);
+      } else {
+         char addrbuf[INET_NTOA_BUF_LEN];
+         struct in_addr addr;
+         addr.s_addr = htonl(p->real_addr4);
+         tor_inet_ntoa(&addr, addrbuf, sizeof(addrbuf));
+         log_debug(LD_REND,"Hidden service maps ipv4 port %d to %s:%d",
+                   p->virtual_port, addrbuf, p->real_port);
+      }
     }
   }
 }
@@ -193,7 +204,6 @@
   int virtport;
   int realport;
   uint16_t p;
-  uint32_t addr;
   const char *addrport;
   rend_service_port_config_t *result = NULL;
 
@@ -212,32 +222,60 @@
     goto err;
   }
 
+  result = tor_malloc(sizeof(rend_service_port_config_t));
+  result->virtual_port = virtport;
+
   if (smartlist_len(sl) == 1) {
     /* No addr:port part; use default. */
-    realport = virtport;
-    addr = 0x7F000001u; /* 127.0.0.1 */
+    result->real_family = AF_INET;
+    result->real_port = virtport;
+    result->real_addr4 = 0x7F000001u; /* 127.0.0.1 */
+    log_warn(LD_CONFIG,"no address nor port in hidden service port "
+                 "configuration->localhost.");
   } else {
     addrport = smartlist_get(sl,1);
     if (strchr(addrport, ':') || strchr(addrport, '.')) {
-      if (parse_addr_port(LOG_WARN, addrport, NULL, &addr, &p)<0) {
-        log_warn(LD_CONFIG,"Unparseable address in hidden service port "
+      uint32_t addr4;
+      if (parse_addr_port(LOG_WARN, addrport, NULL, &addr4, &p)<0) {
+        struct in6_addr addr6;
+        if (parse_addr6_port(LOG_WARN, addrport, NULL, &addr6, &p)<0) {
+           log_warn(LD_CONFIG,"Unparseable address in hidden service port "
                  "configuration.");
-        goto err;
+           goto err;
+	} else {
+         // ipv6-address could be parsed
+         result->real_family = AF_INET6;
+         result->real_addr6 = addr6;
+
+         char addrbuf[42];
+         struct in6_addr addr;
+         addr = result->real_addr6;
+         tor_inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf));
+           log_warn(LD_CONFIG,"parse_port_config() parseable ipv6-address %s in hidden service port "
+                 "configuration.", addrbuf);
+	}
+      } else {
+         // ipv4-address could be parsed
+         result->real_family = AF_INET;
+         result->real_addr4 = addr4;
+           log_warn(LD_CONFIG,"parse_port_config() parseable ipv4-address in hidden service port "
+                 "configuration.");
       }
       realport = p?p:virtport;
+      result->real_port = realport;
     } else {
       /* No addr:port, no addr -- must be port. */
       realport = atoi(addrport);
       if (realport < 1 || realport > 65535)
         goto err;
-      addr = 0x7F000001u; /* Default to 127.0.0.1 */
+      result->real_family = AF_INET;
+      result->real_port = realport;
+      result->real_addr4 = 0x7F000001u; /* 127.0.0.1 */
+           log_warn(LD_CONFIG,"no address in hidden service port "
+                 "configuration->localhost.");
     }
   }
 
-  result = tor_malloc(sizeof(rend_service_port_config_t));
-  result->virtual_port = virtport;
-  result->real_port = realport;
-  result->real_addr = addr;
  err:
   SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
   smartlist_free(sl);
@@ -286,6 +324,7 @@
     if (!strcasecmp(line->key, "HiddenServicePort")) {
       portcfg = parse_port_config(line->value);
       if (!portcfg) {
+        log_warn(LD_CONFIG, "unparsable HiddenServicePort %s", line->value);
         rend_service_free(service);
         return -1;
       }
@@ -387,7 +426,7 @@
     const char *name = smartlist_get(service->intro_nodes, i);
     router = router_get_by_nickname(name, 1);
     if (!router) {
-      log_info(LD_REND,"Router '%s' not found for intro point %d. Skipping.",
+      log_info(LD_REND,"Router '%s' not found for hidden service intro point %d. Skipping.",
                safe_str(name), i);
       continue;
     }
@@ -428,7 +467,7 @@
     if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
         strlcat(fname,PATH_SEPARATOR"private_key",sizeof(fname))
                                                   >= sizeof(fname)) {
-      log_warn(LD_CONFIG, "Directory name too long to store key file: \"%s\".",
+      log_warn(LD_CONFIG, "Directory name too long to store key file: \"%s\" for hidden service.",
                s->directory);
       return -1;
     }
@@ -438,17 +477,17 @@
 
     /* Create service file */
     if (rend_get_service_id(s->private_key, s->service_id)<0) {
-      log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
+      log_warn(LD_BUG, "Internal error: couldn't encode hidden service ID.");
       return -1;
     }
     if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) {
-      log_warn(LD_BUG, "Couldn't compute hash of public key.");
+      log_warn(LD_BUG, "Couldn't compute hash of public key for hidden service.");
       return -1;
     }
     if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
         strlcat(fname,PATH_SEPARATOR"hostname",sizeof(fname))
                                                   >= sizeof(fname)) {
-      log_warn(LD_CONFIG, "Directory name too long to store hostname file:"
+      log_warn(LD_CONFIG, "Directory name too long to store hostname file for hidden service:"
                " \"%s\".", s->directory);
       return -1;
     }
@@ -467,9 +506,11 @@
 rend_service_get_by_pk_digest_and_version(const char* digest,
                                           uint8_t version)
 {
+  log_warn(LD_BUG, "Looking up hidden service for digest %s with version %i.", digest, version);
   SMARTLIST_FOREACH(rend_service_list, rend_service_t*, s,
                     if (!memcmp(s->pk_digest,digest,DIGEST_LEN) &&
                         s->descriptor_version == version) return s);
+  log_warn(LD_BUG, "Couldn't find hidden service for digest %s with version %i.", digest, version);
   return NULL;
 }
 
@@ -521,12 +562,12 @@
 
   base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
                 circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
-  log_info(LD_REND, "Received INTRODUCE2 cell for service %s on circ %d.",
+  log_info(LD_REND, "Received INTRODUCE2 cell for hidden service %s on circ %d.",
            escaped(serviceid), circuit->_base.n_circ_id);
 
   if (circuit->_base.purpose != CIRCUIT_PURPOSE_S_INTRO) {
     log_warn(LD_PROTOCOL,
-             "Got an INTRODUCE2 over a non-introduction circuit %d.",
+             "Got an INTRODUCE2 over a non-introduction to hidden service circuit %d.",
              circuit->_base.n_circ_id);
     return -1;
   }
@@ -534,7 +575,7 @@
   /* min key length plus digest length plus nickname length */
   if (request_len < DIGEST_LEN+REND_COOKIE_LEN+(MAX_NICKNAME_LEN+1)+
       DH_KEY_LEN+42) {
-    log_warn(LD_PROTOCOL, "Got a truncated INTRODUCE2 cell on circ %d.",
+    log_warn(LD_PROTOCOL, "Got a truncated INTRODUCE2 cell on hidden service circ %d.",
              circuit->_base.n_circ_id);
     return -1;
   }
@@ -543,7 +584,7 @@
   service = rend_service_get_by_pk_digest_and_version(
               circuit->rend_pk_digest, circuit->rend_desc_version);
   if (!service) {
-    log_warn(LD_REND, "Got an INTRODUCE2 cell for an unrecognized service %s.",
+    log_warn(LD_REND, "Got an INTRODUCE2 cell for an unrecognized hidden service %s.",
              escaped(serviceid));
     return -1;
   }
@@ -560,7 +601,7 @@
   if (memcmp(intro_key_digest, request, DIGEST_LEN)) {
     base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
                   request, REND_SERVICE_ID_LEN);
-    log_warn(LD_REND, "Got an INTRODUCE2 cell for the wrong service (%s).",
+    log_warn(LD_REND, "Got an INTRODUCE2 cell for the wrong hidden service (%s).",
              escaped(serviceid));
     return -1;
   }
@@ -568,7 +609,7 @@
   keylen = crypto_pk_keysize(intro_key);
   if (request_len < keylen+DIGEST_LEN) {
     log_warn(LD_PROTOCOL,
-             "PK-encrypted portion of INTRODUCE2 cell was truncated.");
+             "PK-encrypted portion of INTRODUCE2 hidden service cell was truncated.");
     return -1;
   }
   /* Next N bytes is encrypted with service key */
@@ -577,7 +618,7 @@
        intro_key,buf,request+DIGEST_LEN,request_len-DIGEST_LEN,
        PK_PKCS1_OAEP_PADDING,1);
   if (r<0) {
-    log_warn(LD_PROTOCOL, "Couldn't decrypt INTRODUCE2 cell.");
+    log_warn(LD_PROTOCOL, "Couldn't decrypt INTRODUCE2 hidden service cell.");
     return -1;
   }
   len = r;
@@ -585,7 +626,8 @@
     /* Version 2 INTRODUCE2 cell. */
     int klen;
     extend_info = tor_malloc_zero(sizeof(extend_info_t));
-    extend_info->addr = ntohl(get_uint32(buf+1));
+    extend_info->family = AF_INET;
+    extend_info->addr4 = ntohl(get_uint32(buf+1));
     extend_info->port = ntohs(get_uint16(buf+5));
     memcpy(extend_info->identity_digest, buf+7, DIGEST_LEN);
     extend_info->nickname[0] = '$';
@@ -594,7 +636,7 @@
 
     klen = ntohs(get_uint16(buf+7+DIGEST_LEN));
     if ((int)len != 7+DIGEST_LEN+2+klen+20+128) {
-      log_warn(LD_PROTOCOL, "Bad length %u for version 2 INTRODUCE2 cell.",
+      log_warn(LD_PROTOCOL, "Bad length %u for version 2 INTRODUCE2 hidden service cell.",
                (int)len);
       reason = END_CIRC_REASON_TORPROTOCOL;
       goto err;
@@ -602,7 +644,7 @@
     extend_info->onion_key = crypto_pk_asn1_decode(buf+7+DIGEST_LEN+2, klen);
     if (!extend_info->onion_key) {
       log_warn(LD_PROTOCOL,
-               "Error decoding onion key in version 2 INTRODUCE2 cell.");
+               "Error decoding onion key in version 2 INTRODUCE2 hidden service cell.");
       reason = END_CIRC_REASON_TORPROTOCOL;
       goto err;
     }
@@ -625,12 +667,12 @@
     ptr=memchr(rp_nickname,0,nickname_field_len);
     if (!ptr || ptr == rp_nickname) {
       log_warn(LD_PROTOCOL,
-               "Couldn't find a nul-padded nickname in INTRODUCE2 cell.");
+               "Couldn't find a nul-padded nickname in INTRODUCE2 hidden service cell.");
       return -1;
     }
     if ((version == 0 && !is_legal_nickname(rp_nickname)) ||
         (version == 1 && !is_legal_nickname_or_hexdigest(rp_nickname))) {
-      log_warn(LD_PROTOCOL, "Bad nickname in INTRODUCE2 cell.");
+      log_warn(LD_PROTOCOL, "Bad nickname in INTRODUCE2 hidden service cell.");
       return -1;
     }
     /* Okay, now we know that a nickname is at the start of the buffer. */
@@ -640,7 +682,7 @@
                                * any */
     router = router_get_by_nickname(rp_nickname, 0);
     if (!router) {
-      log_info(LD_REND, "Couldn't find router %s named in introduce2 cell.",
+      log_info(LD_REND, "Couldn't find router %s named in introduce2 hidden service cell.",
                escaped_safe_str(rp_nickname));
       /* XXXX Add a no-such-router reason? */
       reason = END_CIRC_REASON_TORPROTOCOL;
@@ -651,7 +693,7 @@
   }
 
   if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
-    log_warn(LD_PROTOCOL, "Bad length %u for INTRODUCE2 cell.", (int)len);
+    log_warn(LD_PROTOCOL, "Bad length %u for INTRODUCE2 hidden service cell.", (int)len);
     reason = END_CIRC_REASON_TORPROTOCOL;
     return -1;
   }
@@ -663,13 +705,13 @@
   dh = crypto_dh_new();
   if (!dh || crypto_dh_generate_public(dh)<0) {
     log_warn(LD_BUG,"Internal error: couldn't build DH state "
-             "or generate public key.");
+             "or generate public key for hidden service.");
     reason = END_CIRC_REASON_INTERNAL;
     goto err;
   }
   if (crypto_dh_compute_secret(dh, ptr+REND_COOKIE_LEN, DH_KEY_LEN, keys,
                                DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) {
-    log_warn(LD_BUG, "Internal error: couldn't complete DH handshake");
+    log_warn(LD_BUG, "Internal error: couldn't complete DH handshake for hidden service");
     reason = END_CIRC_REASON_INTERNAL;
     goto err;
   }
@@ -691,14 +733,14 @@
   }
   if (!launched) { /* give up */
     log_warn(LD_REND, "Giving up launching first hop of circuit to rendezvous "
-             "point %s for service %s.",
+             "point %s for hidden service %s.",
              escaped_safe_str(extend_info->nickname), serviceid);
     reason = END_CIRC_REASON_CONNECTFAILED;
     goto err;
   }
   log_info(LD_REND,
            "Accepted intro; launching circuit to %s "
-           "(cookie %s) for service %s.",
+           "(cookie %s) for hidden service %s.",
            escaped_safe_str(extend_info->nickname), hexcookie, serviceid);
   tor_assert(launched->build_state);
   /* Fill in the circuit's state. */
@@ -744,7 +786,7 @@
       oldcirc->build_state->failure_count > MAX_REND_FAILURES ||
       oldcirc->build_state->expiry_time < time(NULL)) {
     log_info(LD_REND,
-             "Attempt to build circuit to %s for rendezvous has failed "
+             "Attempt to build circuit to hidden service %s for rendezvous has failed "
              "too many times or expired; giving up.",
              oldcirc->build_state ?
                oldcirc->build_state->chosen_exit->nickname : "*unknown*");
@@ -755,18 +797,18 @@
   tor_assert(oldstate);
 
   if (oldstate->pending_final_cpath == NULL) {
-    log_info(LD_REND,"Skipping relaunch of circ that failed on its first hop. "
+    log_info(LD_REND,"Skipping relaunch of circ that failed on its first hop for hidden service. "
              "Initiator will retry.");
     return;
   }
 
-  log_info(LD_REND,"Reattempting rendezvous circuit to '%s'",
+  log_info(LD_REND,"Reattempting rendezvous hidden service circuit to '%s'",
            oldstate->chosen_exit->nickname);
 
   newcirc = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_CONNECT_REND, 0,
                                oldstate->chosen_exit, 0, 1, 1);
   if (!newcirc) {
-    log_warn(LD_REND,"Couldn't relaunch rendezvous circuit to '%s'.",
+    log_warn(LD_REND,"Couldn't relaunch rendezvous hidden service circuit to '%s'.",
              oldstate->chosen_exit->nickname);
     return;
   }
@@ -796,7 +838,7 @@
   origin_circuit_t *launched;
 
   log_info(LD_REND,
-           "Launching circuit to introduction point %s for service %s",
+           "Launching circuit to introduction point %s for hidden service %s",
            escaped_safe_str(nickname), service->service_id);
 
   rep_hist_note_used_internal(time(NULL), 1, 0);
@@ -806,7 +848,7 @@
                                         nickname, 1, 0, 1);
   if (!launched) {
     log_info(LD_REND,
-             "Can't launch circuit to establish introduction at %s.",
+             "Can't launch circuit to establish introduction for hidden service at %s.",
              escaped_safe_str(nickname));
     return -1;
   }
@@ -849,14 +891,14 @@
   service = rend_service_get_by_pk_digest_and_version(
               circuit->rend_pk_digest, circuit->rend_desc_version);
   if (!service) {
-    log_warn(LD_REND, "Unrecognized service ID %s on introduction circuit %d.",
+    log_warn(LD_REND, "Unrecognized hidden service ID %s on introduction circuit %d.",
              serviceid, circuit->_base.n_circ_id);
     reason = END_CIRC_REASON_NOSUCHSERVICE;
     goto err;
   }
 
   log_info(LD_REND,
-           "Established circuit %d as introduction point for service %s",
+           "Established circuit %d as introduction point for hidden service %s",
            circuit->_base.n_circ_id, serviceid);
 
   /* If the introduction point will not be used in an unversioned
@@ -879,7 +921,7 @@
   note_crypto_pk_op(REND_SERVER);
   r = crypto_pk_private_sign_digest(intro_key, buf+len, buf, len);
   if (r<0) {
-    log_warn(LD_BUG, "Internal error: couldn't sign introduction request.");
+    log_warn(LD_BUG, "Internal error: couldn't sign introduction hidden service request.");
     reason = END_CIRC_REASON_INTERNAL;
     goto err;
   }
@@ -889,7 +931,7 @@
                                    RELAY_COMMAND_ESTABLISH_INTRO,
                                    buf, len, circuit->cpath->prev)<0) {
     log_info(LD_GENERAL,
-             "Couldn't send introduction request for service %s on circuit %d",
+             "Couldn't send introduction request for hidden service %s on circuit %d",
              serviceid, circuit->_base.n_circ_id);
     reason = END_CIRC_REASON_INTERNAL;
     goto err;
@@ -914,13 +956,13 @@
 
   if (circuit->_base.purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) {
     log_warn(LD_PROTOCOL,
-             "received INTRO_ESTABLISHED cell on non-intro circuit.");
+             "received INTRO_ESTABLISHED cell on non-intro hidden service circuit.");
     goto err;
   }
   service = rend_service_get_by_pk_digest_and_version(
               circuit->rend_pk_digest, circuit->rend_desc_version);
   if (!service) {
-    log_warn(LD_REND, "Unknown service on introduction circuit %d.",
+    log_warn(LD_REND, "Unknown hidden service on introduction circuit %d.",
              circuit->_base.n_circ_id);
     goto err;
   }
@@ -930,7 +972,7 @@
   base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32 + 1,
                 circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
   log_info(LD_REND,
-           "Received INTRO_ESTABLISHED cell on circuit %d for service %s",
+           "Received INTRO_ESTABLISHED cell on circuit %d for hidden service %s",
            circuit->_base.n_circ_id, serviceid);
 
   return 0;
@@ -964,13 +1006,13 @@
 
   log_info(LD_REND,
            "Done building circuit %d to rendezvous with "
-           "cookie %s for service %s",
+           "cookie %s for hidden service %s",
            circuit->_base.n_circ_id, hexcookie, serviceid);
 
   service = rend_service_get_by_pk_digest_and_version(
               circuit->rend_pk_digest, circuit->rend_desc_version);
   if (!service) {
-    log_warn(LD_GENERAL, "Internal error: unrecognized service ID on "
+    log_warn(LD_GENERAL, "Internal error: unrecognized hidden service ID on "
              "introduction circuit.");
     reason = END_CIRC_REASON_INTERNAL;
     goto err;
@@ -980,7 +1022,7 @@
   memcpy(buf, circuit->rend_cookie, REND_COOKIE_LEN);
   if (crypto_dh_get_public(hop->dh_handshake_state,
                            buf+REND_COOKIE_LEN, DH_KEY_LEN)<0) {
-    log_warn(LD_GENERAL,"Couldn't get DH public key.");
+    log_warn(LD_GENERAL,"Couldn't get DH public key for hidden service.");
     reason = END_CIRC_REASON_INTERNAL;
     goto err;
   }
@@ -992,7 +1034,7 @@
                                    RELAY_COMMAND_RENDEZVOUS1,
                                    buf, REND_COOKIE_LEN+DH_KEY_LEN+DIGEST_LEN,
                                    circuit->cpath->prev)<0) {
-    log_warn(LD_GENERAL, "Couldn't send RENDEZVOUS1 cell.");
+    log_warn(LD_GENERAL, "Couldn't send RENDEZVOUS1 cell for hidden service.");
     reason = END_CIRC_REASON_INTERNAL;
     goto err;
   }
@@ -1078,7 +1120,7 @@
     if (rend_encode_service_descriptor(service->desc,
                                        service->private_key,
                                        &desc, &desc_len)<0) {
-      log_warn(LD_BUG, "Internal error: couldn't encode service descriptor; "
+      log_warn(LD_BUG, "Internal error: couldn't encode hidden service descriptor; "
                "not uploading.");
       return;
     }
@@ -1107,7 +1149,7 @@
       seconds_valid = rend_encode_v2_descriptors(desc_strs, desc_ids,
                       service->desc, now, NULL, 0);
       if (seconds_valid < 0) {
-        log_warn(LD_BUG, "Internal error: couldn't encode service descriptor; "
+        log_warn(LD_BUG, "Internal error: couldn't encode hidden service descriptor; "
                  "not uploading.");
         return;
       }
@@ -1138,8 +1180,8 @@
                                                    service->desc, now,
                                                    NULL, 1);
         if (seconds_valid < 0) {
-          log_warn(LD_BUG, "Internal error: couldn't encode service "
-                   "descriptor; not uploading.");
+          log_warn(LD_BUG, "Internal error: couldn't encode hidden service "
+                   "descriptor for service %s; not uploading.", serviceid);
           return;
         }
         directory_post_to_hs_dir(desc_ids, desc_strs, serviceid,
@@ -1153,7 +1195,7 @@
         smartlist_free(desc_ids);
       }
       uploaded = 1;
-      log_info(LD_REND, "Successfully uploaded v2 rend descriptors!");
+      log_info(LD_REND, "Successfully uploaded v2 rend hidden service descriptors for service %s!", serviceid);
     }
   }
 
@@ -1242,7 +1284,7 @@
                0, 0);
       if (!router) {
         log_warn(LD_REND,
-                 "Could only establish %d introduction points for %s.",
+                 "Could only establish %d introduction points for hidden service %s.",
                  smartlist_len(service->intro_nodes), service->service_id);
         break;
       }
@@ -1255,7 +1297,7 @@
       smartlist_add(intro_routers, router);
       smartlist_add(exclude_routers, router);
       smartlist_add(service->intro_nodes, hex_digest);
-      log_info(LD_REND, "Picked router %s as an intro point for %s.",
+      log_info(LD_REND, "Picked router %s as an intro point for hidden service %s.",
                router->nickname, service->service_id);
     }
 
@@ -1271,7 +1313,7 @@
       intro = smartlist_get(service->intro_nodes, j);
       r = rend_service_launch_establish_intro(service, intro);
       if (r<0) {
-        log_warn(LD_REND, "Error launching circuit to node %s for service %s.",
+        log_warn(LD_REND, "Error launching circuit to node %s for hidden service %s.",
                  intro, service->service_id);
       }
     }
@@ -1330,7 +1372,7 @@
 
   for (i=0; i < smartlist_len(rend_service_list); ++i) {
     service = smartlist_get(rend_service_list, i);
-    log(severity, LD_GENERAL, "Service configured in \"%s\":",
+    log(severity, LD_GENERAL, "Hidden service configured in \"%s\":",
         service->directory);
     for (j=0; j < smartlist_len(service->intro_nodes); ++j) {
       nickname = smartlist_get(service->intro_nodes, j);
@@ -1373,7 +1415,7 @@
   rend_service_port_config_t *chosen_port;
 
   tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
-  log_debug(LD_REND,"beginning to hunt for addr/port");
+  log_debug(LD_REND,"beginning to hunt for addr/port for hidden ipv4 or ipv6 -service");
   base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
                 circ->rend_pk_digest, REND_SERVICE_ID_LEN);
   service = rend_service_get_by_pk_digest_and_version(circ->rend_pk_digest,
@@ -1394,11 +1436,25 @@
   chosen_port = smartlist_choose(matching_ports);
   smartlist_free(matching_ports);
   if (chosen_port) {
-    conn->_base.addr = chosen_port->real_addr;
+    if (chosen_port->real_family == AF_INET6) {
+      conn->_base.addr6 = chosen_port->real_addr6;
+
+      char addrbuf[42];
+      struct in6_addr addr;
+      addr = conn->_base.addr6;
+      tor_inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf));
+      log_info(LD_REND, "rend_service_set_connection_addr_port() found virtual ipv6-port mapping exist for port %d on service %s on local ipv6 %s",
+           conn->_base.port, serviceid, addrbuf);
+    } else {
+      conn->_base.addr4 = chosen_port->real_addr4;
+      log_info(LD_REND, "rend_service_set_connection_addr_port() found virtual ipv4-port mapping exist for port %d on (ipv4) service %s",
+           conn->_base.port, serviceid);
+    }
+    conn->_base.socket_family = chosen_port->real_family;
     conn->_base.port = chosen_port->real_port;
     return 0;
   }
-  log_info(LD_REND, "No virtual port mapping exists for port %d on service %s",
+  log_info(LD_REND, "No virtual port mapping exists for port %d on hidden ipv4 or ipv6 - service %s",
            conn->_base.port,serviceid);
   return -1;
 }
Index: or/routerlist.c
===================================================================
--- or/routerlist.c	(Revision 12621)
+++ or/routerlist.c	(Arbeitskopie)
@@ -4496,7 +4496,7 @@
 
   /* XXX020 make this louder once we have some v2hidservs */
   log_info(LD_REND, "We don't have enough hidden service directories to "
-           "perform v2 rendezvous operations!");
+           "perform v2 rendezvous operations! id=%s", id);
   return -1;
 }
 
Index: or/connection_edge.c
===================================================================
--- or/connection_edge.c	(Revision 12621)
+++ or/connection_edge.c	(Arbeitskopie)
@@ -204,7 +204,12 @@
   payload[0] = reason;
   if (reason == END_STREAM_REASON_EXITPOLICY &&
       !connection_edge_is_rendezvous_stream(conn)) {
-    set_uint32(payload+1, htonl(conn->_base.addr));
+     if (conn->_base.socket_family == AF_INET6) {
+       log_warn(LD_BUG,
+                "ipv6 not supported in connection_edge_end for non-rendezvous-streams");
+       return 0;
+     }
+    set_uint32(payload+1, htonl(conn->_base.addr4));
     set_uint32(payload+5, htonl(dns_clip_ttl(conn->address_ttl)));
     payload_len += 8;
   }
@@ -280,19 +285,28 @@
 int
 connection_edge_finished_connecting(edge_connection_t *edge_conn)
 {
-  char valbuf[INET_NTOA_BUF_LEN];
   connection_t *conn;
-  struct in_addr in;
 
   tor_assert(edge_conn);
   tor_assert(edge_conn->_base.type == CONN_TYPE_EXIT);
   conn = TO_CONN(edge_conn);
   tor_assert(conn->state == EXIT_CONN_STATE_CONNECTING);
 
-  in.s_addr = htonl(conn->addr);
-  tor_inet_ntoa(&in,valbuf,sizeof(valbuf));
-  log_info(LD_EXIT,"Exit connection to %s:%u (%s) established.",
-           escaped_safe_str(conn->address),conn->port,safe_str(valbuf));
+  if (conn->socket_family == AF_INET6) {
+    char addrbuf[42];
+    struct in6_addr addr;
+    addr = conn->addr6;
+    tor_inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf));
+    log_info(LD_EXIT,"Exit connection to %s:%u (%s) established.",
+             escaped_safe_str(conn->address),conn->port,safe_str(addrbuf));
+  } else {
+    char valbuf[INET_NTOA_BUF_LEN];
+    struct in_addr in;
+    in.s_addr = htonl(conn->addr4);
+    tor_inet_ntoa(&in,valbuf,sizeof(valbuf));
+    log_info(LD_EXIT,"Exit connection to %s:%u (%s) established.",
+             escaped_safe_str(conn->address),conn->port,safe_str(valbuf));
+  }
 
   conn->state = EXIT_CONN_STATE_OPEN;
   connection_watch_events(conn, EV_READ); /* stop writing, continue reading */
@@ -305,8 +319,17 @@
                                      RELAY_COMMAND_CONNECTED, NULL, 0) < 0)
       return 0; /* circuit is closed, don't continue */
   } else {
+    if (conn->socket_family == AF_INET6) {
+      char addrbuf[42];
+      struct in6_addr addr;
+      addr = conn->addr6;
+      tor_inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf));
+      log_info(LD_EXIT,"ipv6 not supported for exit connection to %s:%u (%s) that is not a rendezvous_stream.",
+             escaped_safe_str(conn->address),conn->port,safe_str(addrbuf));
+      return -1;
+    }
     char connected_payload[8];
-    set_uint32(connected_payload, htonl(conn->addr));
+    set_uint32(connected_payload, htonl(conn->addr4));
     set_uint32(connected_payload+4,
                htonl(dns_clip_ttl(edge_conn->address_ttl)));
     if (connection_edge_send_command(edge_conn,
@@ -359,7 +382,7 @@
       continue;
     conn = TO_EDGE_CONN(c);
     /* if it's an internal linked connection, don't yell its status. */
-    severity = (!conn->_base.addr && !conn->_base.port)
+    severity = ((conn->_base.socket_family == AF_INET && !conn->_base.addr4) && !conn->_base.port)
       ? LOG_INFO : LOG_NOTICE;
     seconds_idle = now - conn->_base.timestamp_lastread;
 
@@ -1991,7 +2014,7 @@
   }
 
   conn->_base.address = tor_strdup("(Tor_internal)");
-  conn->_base.addr = 0;
+  conn->_base.addr4 = 0;
   conn->_base.port = 0;
 
   if (connection_add(TO_CONN(conn)) < 0) { /* no space, forget it */
@@ -2371,8 +2394,11 @@
 
   if (rh.command == RELAY_COMMAND_BEGIN_DIR) {
     tor_assert(or_circ);
-    if (or_circ->p_conn && or_circ->p_conn->_base.addr)
-      n_stream->_base.addr = or_circ->p_conn->_base.addr;
+    if (or_circ->p_conn && (or_circ->p_conn->_base.socket_family != AF_INET || or_circ->p_conn->_base.addr4)) {
+       n_stream->_base.socket_family = or_circ->p_conn->_base.socket_family;
+       n_stream->_base.addr4 = or_circ->p_conn->_base.addr4;
+       n_stream->_base.addr6 = or_circ->p_conn->_base.addr6;
+      }
     return connection_exit_connect_dir(n_stream);
   }
 
@@ -2444,6 +2470,12 @@
   return 0;
 }
 
+/** compare the 2 given ipv6-addresses
+ *  *
+ *   * returns 0 if they are equal.
+ *    */
+int compare_in6_addr(struct in6_addr a, struct in6_addr b);
+
 /** Connect to conn's specified addr and port. If it worked, conn
  * has now been added to the connection_array.
  *
@@ -2454,7 +2486,9 @@
 void
 connection_exit_connect(edge_connection_t *edge_conn)
 {
-  uint32_t addr;
+  uint32_t addr4;
+  struct in6_addr addr6;
+  int addr_family = AF_INET;
   uint16_t port;
   connection_t *conn = TO_CONN(edge_conn);
 
@@ -2468,19 +2502,33 @@
     return;
   }
 
-  addr = conn->addr;
   port = conn->port;
+  if (conn->socket_family == AF_INET6) {
+    addr6 = conn->addr6;
+    addr_family = AF_INET6;
+  } else {
+    addr4 = conn->addr4;
+  }
   if (redirect_exit_list) {
     SMARTLIST_FOREACH(redirect_exit_list, exit_redirect_t *, r,
-    {
-      if (!addr_mask_cmp_bits(addr, r->addr, r->maskbits) &&
+    {/*
+          if (!addr_mask_cmp_bits(addr, r->addr, r->maskbits) &&
+	            (r->port_min <= port) && (port <= r->port_max)) {
+      */
+      if (
+          (conn->socket_family == AF_INET/*r->family*/ &&
+           ((conn->socket_family == AF_INET && !addr_mask_cmp_bits(addr4, r->addr, r->maskbits)) /*&&
+            (conn->socket_family == AF_INET6 && !compare_in6_addr(addr6, r->addr6))*/)
+	  ) &&
           (r->port_min <= port) && (port <= r->port_max)) {
-        struct in_addr in;
+
         if (r->is_redirect) {
           char tmpbuf[INET_NTOA_BUF_LEN];
-          addr = r->addr_dest;
+          addr_family = AF_INET;
+          addr4 = r->addr_dest;
           port = r->port_dest;
-          in.s_addr = htonl(addr);
+          struct in_addr in;
+          in.s_addr = htonl(addr4);
           tor_inet_ntoa(&in, tmpbuf, sizeof(tmpbuf));
           log_debug(LD_EXIT, "Redirecting connection from %s:%d to %s:%d",
                     escaped_safe_str(conn->address), conn->port,
@@ -2492,7 +2540,13 @@
   }
 
   log_debug(LD_EXIT,"about to try connecting");
-  switch (connection_connect(conn, conn->address, addr, port)) {
+  int s = -1;
+  if (addr_family == AF_INET) {
+     s = connection_connect(conn, conn->address, addr4, port);
+  } else {
+     s = connection_connect6(conn, conn->address, addr6, port);
+  }
+  switch (s) {
     case -1:
       connection_edge_end_errno(edge_conn);
       circuit_detach_stream(circuit_get_by_edge_conn(edge_conn), edge_conn);
@@ -2525,8 +2579,11 @@
                                  NULL, 0);
   } else { /* normal stream */
     /* This must be the original address, not the redirected address. */
+    if (conn->socket_family == AF_INET6) {
+      log_warn(LD_BUG,"we do not support ipv6 in connection_exit_connect!");
+    }
     char connected_payload[8];
-    set_uint32(connected_payload, htonl(conn->addr));
+    set_uint32(connected_payload, htonl(conn->addr4));
     set_uint32(connected_payload+4,
                htonl(dns_clip_ttl(edge_conn->address_ttl)));
     connection_edge_send_command(edge_conn,
@@ -2554,7 +2611,8 @@
 
   dirconn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR, AF_INET));
 
-  dirconn->_base.addr = 0x7f000001;
+  dirconn->_base.socket_family = AF_INET;
+  dirconn->_base.addr4 = 0x7f000001;
   dirconn->_base.port = 0;
   dirconn->_base.address = tor_strdup("Tor network");
   dirconn->_base.type = CONN_TYPE_DIR;
Index: or/dns.c
===================================================================
--- or/dns.c	(Revision 12621)
+++ or/dns.c	(Arbeitskopie)
@@ -413,7 +413,7 @@
     {
     case RESOLVED_TYPE_IPV4:
       buf[1] = 4;
-      set_uint32(buf+2, htonl(conn->_base.addr));
+      set_uint32(buf+2, htonl(conn->_base.addr4));
       set_uint32(buf+6, htonl(ttl));
       buflen = 10;
       break;
@@ -627,7 +627,8 @@
   /* first check if exitconn->_base.address is an IP. If so, we already
    * know the answer. */
   if (tor_inet_aton(exitconn->_base.address, &in) != 0) {
-    exitconn->_base.addr = ntohl(in.s_addr);
+    exitconn->_base.socket_family = AF_INET;
+    exitconn->_base.addr4 = ntohl(in.s_addr);
     exitconn->address_ttl = DEFAULT_DNS_TTL;
     return 1;
   }
@@ -694,7 +695,8 @@
           tor_assert(is_resolve);
           *hostname_out = tor_strdup(resolve->result.hostname);
         } else {
-          exitconn->_base.addr = resolve->result.addr;
+	  exitconn->_base.socket_family = AF_INET;
+          exitconn->_base.addr4 = resolve->result.addr;
         }
         return 1;
       case CACHE_STATE_CACHED_FAILED:
@@ -1000,7 +1002,8 @@
     pendconn = pend->conn; /* don't pass complex things to the
                               connection_mark_for_close macro */
     assert_connection_ok(TO_CONN(pendconn),time(NULL));
-    pendconn->_base.addr = addr;
+    pendconn->_base.socket_family = AF_INET;
+    pendconn->_base.addr4 = addr;
     pendconn->address_ttl = ttl;
 
     if (outcome != DNS_RESOLVE_SUCCEEDED) {
Index: or/or.h
===================================================================
--- or/or.h	(Revision 12621)
+++ or/or.h	(Arbeitskopie)
@@ -851,8 +851,9 @@
   int socket_family; /**< Address family of this connection's socket.  Usually
                       * AF_INET, but it can also be AF_UNIX, or in the future
                       * AF_INET6 */
-  uint32_t addr; /**< IP of the other side of the connection; used to identify
+  uint32_t addr4; /**< IP of the other side of the connection; used to identify
                   * routers, along with port. */
+  struct in6_addr addr6;
   uint16_t port; /**< If non-zero, port  on the other end
                   * of the connection. */
   uint16_t marked_for_close; /**< Should we close this conn on the next
@@ -913,7 +914,9 @@
    * recent, we can rate limit it further. */
   time_t client_used;
 
-  uint32_t real_addr; /**DOCDOC */
+  int real_socket_family; /**< AF_INET or AF_INET6 */
+  uint32_t real_addr4; /**< DOCDOC*/
+  struct in6_addr real_addr6;
 
   circ_id_type_t circ_id_type:2; /**< When we send CREATE cells along this
                                   * connection, which half of the space should
@@ -1576,8 +1579,10 @@
   char nickname[MAX_HEX_NICKNAME_LEN+1]; /**< This router's nickname for
                                           * display. */
   char identity_digest[DIGEST_LEN]; /**< Hash of this router's identity key. */
-  uint16_t port; /**< OR port. */
-  uint32_t addr; /**< IP address in host order. */
+  uint16_t port;         /**< OR port. */
+  uint32_t addr4;        /**< IPv4 address in host order. */
+  struct in6_addr addr6; /**< IPv6 address. */
+  u_int16_t family;      /**< AF_INET6 or AF_INET */
   crypto_pk_env_t *onion_key; /**< Current onionskin key. */
 } extend_info_t;
 
@@ -1737,7 +1742,11 @@
   /** The port for the OR that is next in this circuit. */
   uint16_t n_port;
   /** The IPv4 address of the OR that is next in this circuit. */
-  uint32_t n_addr;
+  uint32_t n_addr4;
+  /** The IPv6 address of the OR that is next in this circuit. */
+  struct in6_addr n_addr6;
+  /**  AF_INET6 or AF_INET */
+  u_int16_t n_family;
 
   /** True iff we are waiting for n_conn_cells to become less full before
    * allowing p_streams to add any more cells. (Origin circuit only.) */
@@ -2623,6 +2632,8 @@
 
 int connection_connect(connection_t *conn, const char *address, uint32_t addr,
                        uint16_t port);
+int connection_connect6(connection_t *conn, const char *address, struct in6_addr addr,
+                       uint16_t port);
 int retry_all_listeners(smartlist_t *replaced_conns,
                         smartlist_t *new_conns);
 
Index: or/connection.c
===================================================================
--- or/connection.c	(Revision 12621)
+++ or/connection.c	(Arbeitskopie)
@@ -645,6 +645,44 @@
   });
 }
 
+/** Create an AF_INET6 listenaddr struct.
+ * <b>listenaddress</b> provides the host and optionally the port information
+ * for the new structure.  If no port is provided in <b>listenaddress</b> then
+ * <b>listenport</b> is used.
+ *
+ * If not NULL <b>readable_addrress</b> will contain a copy of the host part of
+ * <b>listenaddress</b>.
+ *
+ * The listenaddr struct has to be freed by the caller.
+ */
+static struct sockaddr_in6 *
+create_inet6_sockaddr(const char *listenaddress, uint16_t listenport,
+                     char **readable_address) {
+  struct sockaddr_in6 *listenaddr = NULL;
+  struct in6_addr addr6;
+  uint16_t usePort = 0;
+
+  if (parse_addr6_port(LOG_WARN,
+                      listenaddress, readable_address, &addr6, &usePort)<0) {
+    log_warn(LD_CONFIG,
+             "Error parsing/resolving ipv6 ListenAddress %s", listenaddress);
+    goto err;
+  }
+  if (usePort==0)
+    usePort = listenport;
+
+  listenaddr = tor_malloc_zero(sizeof(struct sockaddr_in6));
+  listenaddr->sin6_addr = addr6;
+  listenaddr->sin6_family = AF_INET;
+  listenaddr->sin6_port = htons((uint16_t) usePort);
+
+  return listenaddr;
+
+ err:
+  tor_free(listenaddr);
+  return NULL;
+}
+
 /** Create an AF_INET listenaddr struct.
  * <b>listenaddress</b> provides the host and optionally the port information
  * for the new structure.  If no port is provided in <b>listenaddress</b> then
@@ -747,7 +785,7 @@
     return NULL;
   }
 
-  if (listensockaddr->sa_family == AF_INET) {
+  if (listensockaddr->sa_family == AF_INET6) {
     int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
 #ifndef MS_WINDOWS
     int one=1;
@@ -756,6 +794,55 @@
       start_reading = 1;
 
     usePort = ntohs( (uint16_t)
+                     ((struct sockaddr_in6 *)listensockaddr)->sin6_port);
+
+    log_notice(LD_NET, "Opening %s on ipv6 %s:%d",
+               conn_type_to_string(type), address, usePort);
+
+    s = tor_open_socket(PF_INET6,
+                        is_tcp ? SOCK_STREAM : SOCK_DGRAM,
+                        is_tcp ? IPPROTO_TCP: IPPROTO_UDP);
+    if (s < 0) {
+      log_warn(LD_NET,"ipv6 socket creation failed.");
+      goto err;
+    }
+
+#ifndef MS_WINDOWS
+    /* REUSEADDR on normal places means you can rebind to the port
+     * right after somebody else has let it go. But REUSEADDR on win32
+     * means you can bind to the port _even when somebody else
+     * already has it bound_. So, don't do that on Win32. */
+    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one));
+#endif
+
+    if (bind(s, listensockaddr, sizeof(struct sockaddr_in6)) < 0) {
+      const char *helpfulhint = "";
+      int e = tor_socket_errno(s);
+      if (ERRNO_IS_EADDRINUSE(e))
+        helpfulhint = ". Is Tor already running?";
+      log_warn(LD_NET, "Could not bind to ipv6 %s:%u: %s%s", address, usePort,
+               tor_socket_strerror(e), helpfulhint);
+      tor_close_socket(s);
+      goto err;
+    }
+
+    if (is_tcp) {
+      if (listen(s,SOMAXCONN) < 0) {
+        log_warn(LD_NET, "Could not listen on ipv6 %s:%u: %s", address, usePort,
+                 tor_socket_strerror(tor_socket_errno(s)));
+        tor_close_socket(s);
+        goto err;
+      }
+    }
+  } else if (listensockaddr->sa_family == AF_INET) {
+    int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
+#ifndef MS_WINDOWS
+    int one=1;
+#endif
+    if (is_tcp)
+      start_reading = 1;
+
+    usePort = ntohs( (uint16_t)
                      ((struct sockaddr_in *)listensockaddr)->sin_port);
 
     log_notice(LD_NET, "Opening %s on %s:%d",
@@ -877,23 +964,41 @@
 check_sockaddr_in(struct sockaddr *sa, int len, int level)
 {
   int ok = 1;
-  struct sockaddr_in *sin=(struct sockaddr_in*)sa;
 
-  if (len != sizeof(struct sockaddr_in)) {
-    log_fn(level, LD_NET, "Length of address not as expected: %d vs %d",
-           len,(int)sizeof(struct sockaddr_in));
-    ok = 0;
-  }
-  if (sa->sa_family != AF_INET) {
+  if (sa->sa_family == AF_INET6) {
+     struct sockaddr_in6 *sin=(struct sockaddr_in6*)sa;
+
+     if (len != sizeof(struct sockaddr_in6)) {
+       log_fn(level, LD_NET, "Length of ipv6 address not as expected: %d vs %d",
+              len,(int)sizeof(struct sockaddr_in));
+       ok = 0;
+     }
+
+     if (sin->sin6_port == 0) {
+       log_fn(level, LD_NET,
+              "Address for new connection has port equal to zero.");
+       ok = 0;
+     }
+  } else if (sa->sa_family == AF_INET) {
+     struct sockaddr_in *sin=(struct sockaddr_in*)sa;
+
+     if (len != sizeof(struct sockaddr_in)) {
+       log_fn(level, LD_NET, "Length of ipv4 address not as expected: %d vs %d",
+              len,(int)sizeof(struct sockaddr_in));
+       ok = 0;
+     }
+
+     if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0) {
+       log_fn(level, LD_NET,
+              "Address for new connection has address/port equal to zero.");
+       ok = 0;
+     }
+  } else {
     log_fn(level, LD_NET, "Family of address not as expected: %d vs %d",
            sa->sa_family, AF_INET);
     ok = 0;
   }
-  if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0) {
-    log_fn(level, LD_NET,
-           "Address for new connection has address/port equal to zero.");
-    ok = 0;
-  }
+
   return ok ? 0 : -1;
 }
 
@@ -906,7 +1011,8 @@
   int news; /* the new socket */
   connection_t *newconn;
   /* information about the remote peer when connecting to other routers */
-  struct sockaddr_in remote;
+  struct sockaddr_in remote4;
+  struct sockaddr_in6 remote6;
   char addrbuf[256];
   /* length of the remote address. Must be whatever accept() needs. */
   socklen_t remotelen = sizeof(addrbuf);
@@ -916,7 +1022,7 @@
   tor_assert((size_t)remotelen >= sizeof(struct sockaddr_in));
   memset(addrbuf, 0, sizeof(addrbuf));
 
-  news = accept(conn->s,(struct sockaddr *)&addrbuf,&remotelen);
+  news = accept(conn->s ,(struct sockaddr *)&addrbuf, &remotelen);
   if (news < 0) { /* accept() error */
     int e = tor_socket_errno(conn->s);
     if (ERRNO_IS_ACCEPT_EAGAIN(e)) {
@@ -953,7 +1059,7 @@
     return 0;
   }
 
-  if (conn->socket_family == AF_INET) {
+  if (conn->socket_family == AF_INET || conn->socket_family == AF_INET6) {
     if (check_sockaddr_in((struct sockaddr*)addrbuf, remotelen, LOG_INFO)<0) {
       log_info(LD_NET,
                "accept() returned a strange address; trying getsockname().");
@@ -972,13 +1078,18 @@
         }
       }
     }
-    memcpy(&remote, addrbuf, sizeof(struct sockaddr_in));
+    if (conn->socket_family == AF_INET6) {
+      memcpy(&remote6, addrbuf, sizeof(struct sockaddr_in6));
+    } else {
+      memcpy(&remote4, addrbuf, sizeof(struct sockaddr_in));
+    }
 
     /* process entrance policies here, before we even create the connection */
     if (new_type == CONN_TYPE_AP) {
       /* check sockspolicy to see if we should accept it */
-      if (socks_policy_permits_address(ntohl(remote.sin_addr.s_addr)) == 0) {
-        tor_inet_ntoa(&remote.sin_addr, tmpbuf, sizeof(tmpbuf));
+      //TODO: support ipv6
+      if (conn->socket_family == AF_INET && socks_policy_permits_address(ntohl(remote4.sin_addr.s_addr)) == 0) {
+        tor_inet_ntoa(&remote4.sin_addr, tmpbuf, sizeof(tmpbuf));
         log_notice(LD_APP,
                    "Denying socks connection from untrusted address %s.",
                    tmpbuf);
@@ -988,8 +1099,9 @@
     }
     if (new_type == CONN_TYPE_DIR) {
       /* check dirpolicy to see if we should accept it */
-      if (dir_policy_permits_address(ntohl(remote.sin_addr.s_addr)) == 0) {
-        tor_inet_ntoa(&remote.sin_addr, tmpbuf, sizeof(tmpbuf));
+      //TODO: support ipv6
+      if (conn->socket_family == AF_INET && dir_policy_permits_address(ntohl(remote4.sin_addr.s_addr)) == 0) {
+        tor_inet_ntoa(&remote4.sin_addr, tmpbuf, sizeof(tmpbuf));
         log_notice(LD_DIRSERV,"Denying dir connection from address %s.",
                    tmpbuf);
         tor_close_socket(news);
@@ -1001,9 +1113,16 @@
     newconn->s = news;
 
     /* remember the remote address */
-    newconn->addr = ntohl(remote.sin_addr.s_addr);
-    newconn->port = ntohs(remote.sin_port);
-    newconn->address = tor_dup_addr(newconn->addr);
+    if (conn->socket_family == AF_INET6) {
+      newconn->addr6 = remote6.sin6_addr;
+      newconn->port = ntohs(remote6.sin6_port);
+      newconn->address = tor_dup_addr6(newconn->addr6);
+    } else {
+      newconn->addr4 = remote4.sin_addr.s_addr;
+      newconn->port = ntohs(remote4.sin_port);
+      newconn->address = tor_dup_addr(newconn->addr4);
+    }
+    newconn->socket_family = conn->socket_family;
 
   } else if (conn->socket_family == AF_UNIX) {
     /* For now only control ports can be unix domain sockets
@@ -1014,12 +1133,12 @@
     newconn->s = news;
 
     /* remember the remote address -- do we have anything sane to put here? */
-    newconn->addr = 0;
+    newconn->addr4 = 0;
     newconn->port = 1;
     newconn->address = tor_strdup(conn->address);
   } else {
     tor_assert(0);
-  };
+  }
 
   if (connection_add(newconn) < 0) { /* no space, forget it */
     connection_free(newconn);
@@ -1161,6 +1280,96 @@
   return inprogress ? 0 : 1;
 }
 
+/** Take conn, make a nonblocking socket; try to connect to
+ * addr:port (they arrive in *host order*). If fail, return -1. Else
+ * assign s to conn-\>s: if connected return 1, if EAGAIN return 0.
+ *
+ * address is used to make the logs useful.
+ *
+ * On success, add conn to the list of polled connections.
+ */
+int
+connection_connect6(connection_t *conn, const char *address,
+                   struct in6_addr addr6, uint16_t port)
+{
+  int s, inprogress = 0;
+  struct sockaddr_in6 dest_addr6;
+  or_options_t *options = get_options();
+
+  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);
+    return -1;
+  }
+
+  s = tor_open_socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
+  if (s < 0) {
+    log_warn(LD_NET,"Error creating network socket: %s",
+             tor_socket_strerror(tor_socket_errno(-1)));
+    return -1;
+  }
+
+  if (options->OutboundBindAddress) {
+    struct sockaddr_in6 ext_addr6;
+
+    memset(&ext_addr6, 0, sizeof(ext_addr6));
+    ext_addr6.sin6_family = AF_INET6;
+    ext_addr6.sin6_port = 0;
+    if (!tor_inet_aton6(options->OutboundBindAddress, &ext_addr6.sin6_addr)) {
+      log_warn(LD_CONFIG,"Outbound ipv6 bind address '%s' didn't parse. Ignoring.",
+               options->OutboundBindAddress);
+    } else {
+      if (bind(s, (struct sockaddr*)&ext_addr6, sizeof(ext_addr6)) < 0) {
+        log_warn(LD_NET,"Error binding network ipv6 socket: %s",
+                 tor_socket_strerror(tor_socket_errno(s)));
+        tor_close_socket(s);
+        return -1;
+      }
+    }
+  }
+
+  set_socket_nonblocking(s);
+
+  if (options->ConstrainedSockets)
+    set_constrained_socket_buffers(s, (int)options->ConstrainedSockSize);
+
+  memset(&dest_addr6, 0, sizeof(dest_addr6));
+  dest_addr6.sin6_family = AF_INET6;
+  dest_addr6.sin6_port = htons(port);
+  dest_addr6.sin6_addr = addr6;
+
+  log_debug(LD_NET, "Connecting to ipv6 %s:%u.", escaped_safe_str(address), port);
+
+  if (connect(s, (struct sockaddr *)&dest_addr6, sizeof(dest_addr6)) < 0) {
+    int e = tor_socket_errno(s);
+    if (!ERRNO_IS_CONN_EINPROGRESS(e)) {
+      /* yuck. kill it. */
+      log_info(LD_NET,
+               "connect() to ipv6 %s:%u failed: %s", escaped_safe_str(address),
+               port, tor_socket_strerror(e));
+      tor_close_socket(s);
+      return -1;
+    } else {
+      inprogress = 1;
+    }
+  }
+
+  if (!server_mode(options))
+    client_check_address_changed(s);
+
+  /* it succeeded. we're connected. */
+  log_fn(inprogress?LOG_DEBUG:LOG_INFO, LD_NET,
+         "Connection to ipv6 %s:%u %s (sock %d).", escaped_safe_str(address),
+         port, inprogress?"in progress":"established", s);
+  conn->s = s;
+  if (connection_add(conn) < 0) /* no space, forget it */
+    return -1;
+  return inprogress ? 0 : 1;
+}
+
 /**
  * Launch any configured listener connections of type <b>type</b>.  (A
  * listener is configured if <b>port_option</b> is non-zero.  If any
@@ -1190,7 +1399,7 @@
   connection_t *conn;
   config_line_t *line;
 
-  tor_assert(socket_family == AF_INET || socket_family == AF_UNIX);
+  tor_assert(socket_family == AF_INET || socket_family == AF_INET6 || socket_family == AF_UNIX);
 
   if (cfg && port_option) {
     for (c = cfg; c; c = c->next) {
@@ -1223,6 +1432,19 @@
         char *address=NULL;
         uint16_t port;
         switch (socket_family) {
+          case AF_INET6:
+            if (!parse_addr6_port(LOG_WARN,
+                                 wanted->value, &address, NULL, &port)) {
+              int addr_matches = !strcasecmp(address, conn->address);
+              tor_free(address);
+              if (! port)
+                port = port_option;
+              if (port == conn->port && addr_matches) {
+                line = wanted;
+                break;
+              }
+            }
+            break;
           case AF_INET:
             if (!parse_addr_port(LOG_WARN,
                                  wanted->value, &address, NULL, &port)) {
@@ -1275,6 +1497,12 @@
         struct sockaddr *listensockaddr;
 
         switch (socket_family) {
+          case AF_INET6:
+            listensockaddr = (struct sockaddr *)
+                             create_inet6_sockaddr(cfg_line->value,
+                                                  (uint16_t) port_option,
+                                                  &address);
+            break;
           case AF_INET:
             listensockaddr = (struct sockaddr *)
                              create_inet_sockaddr(cfg_line->value,
@@ -1342,7 +1570,12 @@
                       options->SocksPort, "127.0.0.1",
                       replaced_conns, new_conns, 0,
                       AF_INET)<0)
-    return -1;
+    // maybe the user deliberately configured an ipv6-address to listen on
+    if (retry_listeners(CONN_TYPE_AP_LISTENER, options->SocksListenAddress, 
+                        options->SocksPort, "::1",
+                        replaced_conns, new_conns, 0,
+                        AF_INET6)<0)
+      return -1;
   if (retry_listeners(CONN_TYPE_AP_TRANS_LISTENER, options->TransListenAddress,
                       options->TransPort, "127.0.0.1",
                       replaced_conns, new_conns, 0,
@@ -1380,7 +1613,7 @@
 static int
 connection_is_rate_limited(connection_t *conn)
 {
-  if (conn->linked || is_internal_IP(conn->addr, 0))
+  if (conn->linked || (conn->socket_family == AF_INET && is_internal_IP(conn->addr4, 0)))
     return 0;
   else
     return 1;
@@ -2328,7 +2561,8 @@
   SMARTLIST_FOREACH(conns, connection_t *, conn,
   {
     if (conn->type == CONN_TYPE_OR &&
-        conn->addr == addr &&
+        conn->socket_family == AF_INET &&
+        conn->addr4 == addr &&
         conn->port == port &&
         !conn->marked_for_close &&
         (!best || best->_base.timestamp_created < conn->timestamp_created))
@@ -2348,7 +2582,8 @@
   SMARTLIST_FOREACH(conns, connection_t *, conn,
   {
     if (conn->type == type &&
-        conn->addr == addr &&
+        conn->socket_family == AF_INET &&
+        conn->addr4 == addr &&
         conn->port == port &&
         conn->purpose == purpose &&
         !conn->marked_for_close)
Index: or/rendcommon.c
===================================================================
--- or/rendcommon.c	(Revision 12621)
+++ or/rendcommon.c	(Arbeitskopie)
@@ -227,7 +227,11 @@
       goto done;
     }
     /* Assemble everything for this introduction point. */
-    address = tor_dup_addr(info->addr);
+    if (info->family == AF_INET6) {
+      address = tor_dup_addr6(info->addr6);
+    } else {
+      address = tor_dup_addr(info->addr4);
+    }
     res = tor_snprintf(unenc + unenc_written, unenc_len - unenc_written,
                          "introduction-point %s\n"
                          "ip-address %s\n"
@@ -760,8 +764,11 @@
 {
   char key[REND_SERVICE_ID_LEN_BASE32+2]; /* <version><query>\0 */
   tor_assert(rend_cache);
-  if (!rend_valid_service_id(query))
+  if (!rend_valid_service_id(query)) {
+    log_warn(LD_REND, "hidden service descriptor '%s.onion' ID is not legal.",
+             query);
     return -1;
+  }
   *e = NULL;
   if (version != 0) {
     tor_snprintf(key, sizeof(key), "2%s", query);
@@ -771,8 +778,13 @@
     tor_snprintf(key, sizeof(key), "0%s", query);
     *e = strmap_get_lc(rend_cache, key);
   }
-  if (!*e)
+  if (!*e) {
+    log_warn(LD_REND, "no cached rend_cache_entry_t for hidden service  '%s.onion' with version %i.",
+             query,version);
     return 0;
+  }
+  log_warn(LD_REND, "we have a cached rend_cache_entry_t for hidden service  '%s.onion' with version %i.",
+             query, version);
   return 1;
 }
 
Index: or/directory.c
===================================================================
--- or/directory.c	(Revision 12621)
+++ or/directory.c	(Arbeitskopie)
@@ -478,7 +478,7 @@
     routerinfo_t *me = router_get_my_routerinfo();
     if (me &&
         router_digest_is_me(conn->identity_digest) &&
-        me->addr == conn->_base.addr &&
+        me->addr == conn->_base.addr4 && //TODO: support directories on IPv6
         me->dir_port == conn->_base.port)
       return 1;
   }
@@ -644,7 +644,8 @@
   conn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR, AF_INET));
 
   /* set up conn so it's got all the data we need to remember */
-  conn->_base.addr = addr;
+  conn->_base.socket_family = AF_INET;
+  conn->_base.addr4 = addr;
   conn->_base.port = use_begindir ? or_port : dir_port;
   conn->_base.address = tor_strdup(address);
   memcpy(conn->identity_digest, digest, DIGEST_LEN);
@@ -1940,7 +1941,7 @@
     tor_snprintf(cp, sizeof(tmp)-(cp-tmp), "Content-Type: %s\r\n", type);
     cp += strlen(cp);
   }
-  if (!is_internal_IP(conn->_base.addr, 0)) {
+  if (conn->_base.socket_family == AF_INET && !is_internal_IP(conn->_base.addr4, 0)) { //TODO: support ipv6
     /* Don't report the source address for a localhost/private connection. */
     tor_snprintf(cp, sizeof(tmp)-(cp-tmp),
                  X_ADDRESS_HEADER "%s\r\n", conn->_base.address);
@@ -3121,7 +3122,7 @@
     /* Determine responsible dirs. */
     if (hid_serv_get_responsible_directories(responsible_dirs, desc_id) < 0) {
       log_warn(LD_REND, "Could not determine the responsible hidden service "
-                        "directories to post descriptors to.");
+                        "directories to post descriptors to. desc_id=%s", desc_id);
       smartlist_free(responsible_dirs);
       return;
     }
@@ -3169,7 +3170,7 @@
   if (hid_serv_get_responsible_directories(responsible_dirs, desc_id) < 0) {
     /* XXX020 make this louder once we have some v2hidservs */
     log_info(LD_REND, "Could not determine the responsible hidden service "
-                      "directories to fetch descriptors.");
+                      "directories to fetch descriptors. desc_id=%s", desc_id);
     smartlist_free(responsible_dirs);
     return;
   }
Index: or/routerparse.c
===================================================================
--- or/routerparse.c	(Revision 12621)
+++ or/routerparse.c	(Arbeitskopie)
@@ -3454,7 +3454,8 @@
       tor_free(info);
       goto err;
     }
-    info->addr = ntohl(ip.s_addr);
+    info->family = AF_INET;
+    info->addr4 = ntohl(ip.s_addr);
     /* Parse onion port. */
     tok = find_first_by_keyword(tokens, R_IPO_ONION_PORT);
     info->port = (uint16_t) atoi(tok->args[0]);
Index: or/command.c
===================================================================
--- or/command.c	(Revision 12621)
+++ or/command.c	(Arbeitskopie)
@@ -560,7 +560,7 @@
     }
     if (other_addr_type == RESOLVED_TYPE_IPV4 && other_addr_len == 4) {
       uint32_t addr = ntohl(get_uint32(cp));
-      if (addr == conn->real_addr) {
+      if (conn->real_socket_family == AF_INET && addr == conn->real_addr4) {
         conn->handshake_state->apparently_canonical = 1;
         break;
       }
Index: or/circuituse.c
===================================================================
--- or/circuituse.c	(Revision 12621)
+++ or/circuituse.c	(Arbeitskopie)
@@ -1261,7 +1261,7 @@
   conn_age = time(NULL) - conn->_base.timestamp_created;
 
   if (conn_age >= get_options()->SocksTimeout) {
-    int severity = (!conn->_base.addr && !conn->_base.port) ?
+    int severity = ((conn->_base.socket_family == AF_INET && !conn->_base.addr4) && !conn->_base.port) ?
                      LOG_INFO : LOG_NOTICE;
     log_fn(severity, LD_APP,
            "Tried for %d seconds to get a connection to %s:%d. Giving up.",
Index: or/test.c
===================================================================
--- or/test.c	(Revision 12621)
+++ or/test.c	(Arbeitskopie)
@@ -3311,7 +3311,8 @@
     info->nickname[0] = '$';
     base16_encode(info->nickname + 1, sizeof(info->nickname) - 1,
                   info->identity_digest, DIGEST_LEN);
-    info->addr = crypto_rand_int(65536); /* Does not cover all IP addresses. */
+    info->family = AF_INET;
+    info->addr4 = crypto_rand_int(65536); /* Does not cover all IP addresses. */
     info->port = crypto_rand_int(65536);
     generated->intro_points[i] = tor_strdup(info->nickname);
     generated->intro_point_extend_info[i] = info;
@@ -3346,7 +3347,7 @@
                DIGEST_LEN);
     test_streq(gen_info->nickname, par_info->nickname);
     test_streq(generated->intro_points[i], parsed->intro_points[i]);
-    test_eq(gen_info->addr, par_info->addr);
+    test_eq(gen_info->addr4, par_info->addr4);
     test_eq(gen_info->port, par_info->port);
   }
   tor_free(intro_points_encrypted);
Index: or/router.c
===================================================================
--- or/router.c	(Revision 12621)
+++ or/router.c	(Arbeitskopie)
@@ -1049,11 +1049,13 @@
 
   /* make sure it's resolved to something. this way we can't get a
      'maybe' below. */
-  if (!conn->_base.addr)
+  if (conn->_base.socket_family == AF_INET && !conn->_base.addr4)
     return -1;
 
-  return compare_addr_to_addr_policy(conn->_base.addr, conn->_base.port,
-                   desc_routerinfo->exit_policy) != ADDR_POLICY_ACCEPTED;
+  if (conn->_base.socket_family == AF_INET)
+    return compare_addr_to_addr_policy(conn->_base.addr4, conn->_base.port,
+                     desc_routerinfo->exit_policy) != ADDR_POLICY_ACCEPTED;
+  return 0; //TODO: support an ipv6 exit-policy
 }
 
 /** Return true iff I'm a server and <b>digest</b> is equal to
Index: or/cpuworker.c
===================================================================
--- or/cpuworker.c	(Revision 12621)
+++ or/cpuworker.c	(Arbeitskopie)
@@ -468,7 +468,11 @@
       log_info(LD_OR,"circ->p_conn gone. Failing circ.");
       return -1;
     }
-    tag_pack(tag, circ->p_conn->_base.addr, circ->p_conn->_base.port,
+    if (circ->p_conn->_base.socket_family == AF_INET6) {
+      log_info(LD_OR,"we do not support ipv6 in assign_to_cpuworker().");
+      return -1;
+    }
+    tag_pack(tag, circ->p_conn->_base.addr4, circ->p_conn->_base.port,
              circ->p_circ_id);
 
     cpuworker->state = CPUWORKER_STATE_BUSY_ONION;
Index: or/circuitbuild.c
===================================================================
--- or/circuitbuild.c	(Revision 12621)
+++ or/circuitbuild.c	(Arbeitskopie)
@@ -350,7 +350,11 @@
   tor_assert(firsthop->extend_info);
 
   /* now see if we're already connected to the first OR in 'route' */
-  in.s_addr = htonl(firsthop->extend_info->addr);
+  if (firsthop->extend_info->family == AF_INET6) {
+     log_info(LD_CIRC,"we do not support Ipv6 for firsthop yet. Closing.");
+     return -END_CIRC_REASON_CONNECTFAILED;
+  }
+  in.s_addr = htonl(firsthop->extend_info->addr4);
   tor_inet_ntoa(&in, tmpbuf, sizeof(tmpbuf));
   log_debug(LD_CIRC,"Looking for firsthop '%s:%u'",tmpbuf,
             firsthop->extend_info->port);
@@ -367,11 +371,13 @@
        router_digest_version_as_new_as(firsthop->extend_info->identity_digest,
                                        "0.1.1.9-alpha-cvs"))) {
     /* not currently connected */
-    circ->_base.n_addr = firsthop->extend_info->addr;
-    circ->_base.n_port = firsthop->extend_info->port;
+    circ->_base.n_family = firsthop->extend_info->family;
+    circ->_base.n_addr4  = firsthop->extend_info->addr4;
+    circ->_base.n_addr6  = firsthop->extend_info->addr6;
+    circ->_base.n_port   = firsthop->extend_info->port;
 
     if (!n_conn || n_conn->_base.or_is_obsolete) { /* launch the connection */
-      n_conn = connection_or_connect(firsthop->extend_info->addr,
+      n_conn = connection_or_connect(firsthop->extend_info->addr4,
                                      firsthop->extend_info->port,
                                      firsthop->extend_info->identity_digest);
       if (!n_conn) { /* connect failed, forget the whole thing */
@@ -387,7 +393,9 @@
      */
     return 0;
   } else { /* it's already open. use it. */
-    circ->_base.n_addr = n_conn->_base.addr;
+    circ->_base.n_addr6 = n_conn->_base.addr6;
+    circ->_base.n_addr4 = n_conn->_base.addr4;
+    circ->_base.n_family = n_conn->_base.socket_family;
     circ->_base.n_port = n_conn->_base.port;
     circ->_base.n_conn = n_conn;
     log_debug(LD_CIRC,"Conn open. Delivering first onion skin.");
@@ -399,6 +407,19 @@
   return 0;
 }
 
+/** compare the 2 given ipv6-addresses
+ *
+ * returns 0 if they are equal.
+ */
+int compare_in6_addr(struct in6_addr a, struct in6_addr b) {
+  int i;
+  for (i=0; i<16; i++) {
+    if (a.s6_addr[i] != b.s6_addr[i])
+       return 1;
+  }
+  return 0;
+}
+
 /** Find any circuits that are waiting on <b>or_conn</b> to become
  * open and get them to send their create cells forward.
  *
@@ -426,7 +447,9 @@
         continue;
       if (tor_digest_is_zero(circ->n_conn_id_digest)) {
         /* Look at addr/port. This is an unkeyed connection. */
-        if (circ->n_addr != or_conn->_base.addr ||
+        if (circ->n_family != or_conn->_base.socket_family ||
+	    (circ->n_family == AF_INET  && circ->n_addr4 != or_conn->_base.addr4) ||
+	    (circ->n_family == AF_INET6 && compare_in6_addr(circ->n_addr6, or_conn->_base.addr6) != 0) ||
             circ->n_port != or_conn->_base.port)
           continue;
         /* now teach circ the right identity_digest */
@@ -652,7 +675,12 @@
       return 0;
     }
 
-    set_uint32(payload, htonl(hop->extend_info->addr));
+    if (hop->extend_info->family == AF_INET6) {
+        log(LOG_NOTICE, LD_GENERAL,
+            "circuit_send_next_onion_skin() - we do not support ipv6 for next hops yet. Ignoring.");
+        return 0;
+    }
+    set_uint32(payload, htonl(hop->extend_info->addr4));
     set_uint16(payload+4, htons(hop->extend_info->port));
 
     onionskin = payload+2+4;
@@ -733,7 +761,8 @@
     return -1;
   }
 
-  circ->n_addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE));
+  circ->n_family = AF_INET;
+  circ->n_addr4 = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE));
   circ->n_port = ntohs(get_uint16(cell->payload+RELAY_HEADER_SIZE+4));
 
   onionskin = cell->payload+RELAY_HEADER_SIZE+4+2;
@@ -748,7 +777,11 @@
      router_digest_version_as_new_as(id_digest,"0.1.1.9-alpha-cvs"))) {
     struct in_addr in;
     char tmpbuf[INET_NTOA_BUF_LEN];
-    in.s_addr = htonl(circ->n_addr);
+    if (circ->n_family == AF_INET6) {
+      log_info(LD_CIRC|LD_OR, "Next router is on an ipv6-address. we do not support this yet.");
+      return 0;
+    }
+    in.s_addr = htonl(circ->n_addr4);
     tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
     log_info(LD_CIRC|LD_OR,"Next router (%s:%d) not connected. Connecting.",
              tmpbuf, circ->n_port);
@@ -761,11 +794,18 @@
     memcpy(circ->n_conn_id_digest, id_digest, DIGEST_LEN);
 
     if (n_conn && !n_conn->_base.or_is_obsolete) {
-      circ->n_addr = n_conn->_base.addr;
+      circ->n_family = n_conn->_base.socket_family;
+      circ->n_addr4 = n_conn->_base.addr4;
+      circ->n_addr6 = n_conn->_base.addr6;
       circ->n_port = n_conn->_base.port;
     } else {
      /* we should try to open a connection */
-      n_conn = connection_or_connect(circ->n_addr, circ->n_port, id_digest);
+      if (circ->n_family == AF_INET6) {
+        log_info(LD_CIRC,"Cannot launch n_conn because we do not support ipv6 between nodes yet. Closing circuit.");
+        circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED);
+        return 0;
+      }
+      n_conn = connection_or_connect(circ->n_addr4, circ->n_port, id_digest);
       if (!n_conn) {
         log_info(LD_CIRC,"Launching n_conn failed. Closing circuit.");
         circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED);
@@ -781,7 +821,9 @@
   }
 
   /* these may be different if the router connected to us from elsewhere */
-  circ->n_addr = n_conn->_base.addr;
+  circ->n_family = n_conn->_base.socket_family;
+  circ->n_addr4 = n_conn->_base.addr4;
+  circ->n_addr6 = n_conn->_base.addr6;
   circ->n_port = n_conn->_base.port;
 
   circ->n_conn = n_conn;
@@ -1008,7 +1050,16 @@
                                circ->p_conn, &cell, CELL_DIRECTION_IN);
   log_debug(LD_CIRC,"Finished sending 'created' cell.");
 
-  if (!is_local_IP(circ->p_conn->_base.addr) &&
+  if (circ->p_conn->_base.socket_family == AF_INET6) {
+     log_debug(LD_CIRC,"not checking is_local_IP because we do not support ipv6 yet.");
+     if(connection_or_nonopen_was_started_here(circ->p_conn)) {
+       /* record that we could process create cells from a non-local conn
+        * that we didn't initiate; presumably this means that create cells
+        * can reach us too. */
+       router_orport_found_reachable();
+      }
+  } else
+  if (!is_local_IP(circ->p_conn->_base.addr4) &&
       !connection_or_nonopen_was_started_here(circ->p_conn)) {
     /* record that we could process create cells from a non-local conn
      * that we didn't initiate; presumably this means that create cells
@@ -1749,7 +1800,8 @@
     strlcpy(info->nickname, nickname, sizeof(info->nickname));
   if (onion_key)
     info->onion_key = crypto_pk_dup_key(onion_key);
-  info->addr = addr;
+  info->family = AF_INET;
+  info->addr4 = addr;
   info->port = port;
   return info;
 }
Index: or/dnsserv.c
===================================================================
--- or/dnsserv.c	(Revision 12621)
+++ or/dnsserv.c	(Arbeitskopie)
@@ -116,9 +116,10 @@
   conn->_base.state = AP_CONN_STATE_RESOLVE_WAIT;
   conn->is_dns_request = 1;
 
-  TO_CONN(conn)->addr = ntohl(sin->sin_addr.s_addr);
+  TO_CONN(conn)->socket_family = AF_INET;
+  TO_CONN(conn)->addr4 = ntohl(sin->sin_addr.s_addr);
   TO_CONN(conn)->port = ntohs(sin->sin_port);
-  TO_CONN(conn)->address = tor_dup_addr(TO_CONN(conn)->addr);
+  TO_CONN(conn)->address = tor_dup_addr(TO_CONN(conn)->addr4);
 
   if (q->type == EVDNS_TYPE_A)
     conn->socks_request->command = SOCKS_COMMAND_RESOLVE;
Index: or/rendclient.c
===================================================================
--- or/rendclient.c	(Revision 12621)
+++ or/rendclient.c	(Arbeitskopie)
@@ -117,10 +117,14 @@
   if (entry->parsed->protocols & (1<<2)) {
     /* version 2 format */
     extend_info_t *extend_info = rendcirc->build_state->chosen_exit;
+    if (extend_info->family == AF_INET6) {
+      log_warn(LD_BUG, "we do not support ipv6 in rend_client_send_introduction");
+      goto err;
+      }
     int klen;
     tmp[0] = 2; /* version 2 of the cell format */
     /* nul pads */
-    set_uint32(tmp+1, htonl(extend_info->addr));
+    set_uint32(tmp+1, htonl(extend_info->addr4));
     set_uint16(tmp+5, htons(extend_info->port));
     memcpy(tmp+7, extend_info->identity_digest, DIGEST_LEN);
     klen = crypto_pk_asn1_encode(extend_info->onion_key, tmp+7+DIGEST_LEN+2,
@@ -505,7 +509,7 @@
         entry->parsed->n_intro_points > 0) {
       /* either this fetch worked, or it failed but there was a
        * valid entry from before which we should reuse */
-      log_info(LD_REND,"Rend desc is usable. Launching circuits.");
+      log_info(LD_REND,"Rend desc for hidden service '%s.onion' is usable. Launching circuits.", safe_str(query));
       conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
 
       /* restart their timeout values, so they get a fair shake at
@@ -516,12 +520,12 @@
 
       if (connection_ap_handshake_attach_circuit(conn) < 0) {
         /* it will never work */
-        log_warn(LD_REND,"Rendezvous attempt failed. Closing.");
+        log_warn(LD_REND,"Rendezvous attempt for hidden service '%s.onion' failed. Closing.", safe_str(query));
         connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
       }
     } else { /* 404, or fetch didn't get that far */
       log_notice(LD_REND,"Closing stream for '%s.onion': hidden service is "
-                 "unavailable (try again later).", safe_str(query));
+                 "unavailable (try again later). entry->parsed->n_intro_points=%i", safe_str(query), entry->parsed->n_intro_points);
       connection_mark_unattached_ap(conn, END_STREAM_REASON_RESOLVEFAILED);
     }
   });
Index: common/util.c
===================================================================
--- common/util.c	(Revision 12621)
+++ common/util.c	(Arbeitskopie)
@@ -2223,13 +2223,13 @@
     _address = tor_strndup(addrport, colon-addrport);
     _port = (int) tor_parse_long(colon+1,10,1,65535,NULL,NULL);
     if (!_port) {
-      log_fn(severity, LD_GENERAL, "Port %s out of range", escaped(colon+1));
+      log_fn(severity, LD_GENERAL, "ipv4-Port %s out of range", escaped(colon+1));
       ok = 0;
     }
     if (!port_out) {
       char *esc_addrport = esc_for_log(addrport);
       log_fn(severity, LD_GENERAL,
-             "Port %s given on %s when not required",
+             "ipv4-Port %s given on %s when not required",
              escaped(colon+1), esc_addrport);
       tor_free(esc_addrport);
       ok = 0;
@@ -2241,8 +2241,8 @@
 
   if (addr) {
     /* There's an addr pointer, so we need to resolve the hostname. */
-    if (tor_lookup_hostname(_address,addr)) {
-      log_fn(severity, LD_NET, "Couldn't look up %s", escaped(_address));
+    if (tor_lookup_hostname(_address, addr)) {
+      log_fn(severity, LD_NET, "Couldn't look up ipv4 %s", escaped(_address));
       ok = 0;
       *addr = 0;
     }
@@ -2260,7 +2260,81 @@
 
   return ok ? 0 : -1;
 }
+/** Parse a string of the form "[host]" and "[host]:port" from <b>addrport</b>.  If
+ * <b>address</b> is provided, set *<b>address</b> to a copy of the
+ * host portion of the string.  If <b>addr</b> is provided, try to
+ * resolve the host portion of the string and store it into
+ * *<b>addr</b>.  If <b>port_out</b> is provided,
+ * store the port number into *<b>port_out</b>, or 0 if no port is given.
+ * If <b>port_out</b> is NULL, then there must be no port number in
+ * <b>addrport</b>.
+ * Return 0 on success, -1 on failure.
+ */
+int
+parse_addr6_port(int severity, const char *addrport, char **address,
+                struct in6_addr *addr, uint16_t *port_out)
+{
+  const char *colon;
+  char *_address = NULL;
+  int _port;
+  int ok = 1;
 
+  tor_assert(addrport);
+
+  colon = strstr(addrport, "]:");
+  if (colon) {
+    _address = tor_strndup(addrport, colon - addrport);
+    _port = (int) tor_parse_long(colon + 2, 10, 1, 65535, NULL, NULL);
+    log_fn(severity, LD_NET, "DEBUG ipv6 incl [ and ] is: %s port is %i", escaped(_address), _port);
+    if (!_port) {
+      log_fn(severity, LD_GENERAL, "ipv6-Port %s out of range", escaped(colon+1));
+      ok = 0;
+    }
+    if (!port_out) {
+      char *esc_addrport = esc_for_log(addrport);
+      log_fn(severity, LD_GENERAL,
+             "ipv6-Port %s given on %s when not required",
+             escaped(colon+1), esc_addrport);
+      tor_free(esc_addrport);
+      ok = 0;
+    }
+  } else {
+    _address = tor_strdup(addrport);
+    _port = 0;
+    log_fn(severity, LD_NET, "DEBUG ipv6 incl [ and ] is: %s port is default", escaped(_address));
+  }
+  log_fn(severity, LD_NET, "DEBUG ipv6 incl [ and ] is: %s", escaped(_address));
+  const char *end = strstr(_address, "]");
+  if (end)
+    _address = tor_strndup(_address, end - _address);
+  log_fn(severity, LD_NET, "DEBUG ipv6 incl [ is: %s", escaped(_address));
+  const char *start = strstr(_address, "[");
+  if (start)
+    _address = (char *) start + 1;
+  log_fn(severity, LD_NET, "DEBUG ipv6 without [ is: %s", escaped(_address));
+
+  if (addr) {
+    /* There's an addr pointer, so we need to resolve the hostname. */
+    if (tor_lookup_hostname6(_address, addr)) {
+      log_fn(severity, LD_NET, "Couldn't look up ipv6 %s", escaped(_address));
+      ok = 0;
+      //*addr = 0;
+    }
+  }
+
+  if (address && ok) {
+    *address = _address;
+  } else {
+    if (address)
+      *address = NULL;
+//TODO    tor_free(_address);
+  }
+  if (port_out)
+    *port_out = ok ? ((uint16_t) _port) : 0;
+
+  return ok ? 0 : -1;
+}
+
 /** If <b>mask</b> is an address mask for a bit-prefix, return the number of
  * bits.  Otherwise, return -1. */
 int
@@ -2827,6 +2901,18 @@
  *  and return a strdup of the resulting address.
  */
 char *
+tor_dup_addr6(struct in6_addr addr)
+{
+  char buf[42];
+
+  tor_inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
+  return tor_strdup(buf);
+}
+
+/** Given a host-order <b>addr</b>, call tor_inet_ntop() on it
+ *  and return a strdup of the resulting address.
+ */
+char *
 tor_dup_addr(uint32_t addr)
 {
   char buf[TOR_ADDR_BUF_LEN];
Index: common/util.h
===================================================================
--- common/util.h	(Revision 12621)
+++ common/util.h	(Arbeitskopie)
@@ -271,6 +271,8 @@
 int is_internal_IP(uint32_t ip, int for_listening) ATTR_PURE;
 int parse_addr_port(int severity, const char *addrport, char **address,
                     uint32_t *addr, uint16_t *port_out);
+int parse_addr6_port(int severity, const char *addrport, char **address,
+                    struct in6_addr *addr, uint16_t *port_out);
 int parse_port_range(const char *port, uint16_t *port_min_out,
                      uint16_t *port_max_out);
 int parse_addr_and_port_range(const char *s, uint32_t *addr_out,
@@ -280,6 +282,7 @@
 int addr_mask_cmp_bits(uint32_t a1, uint32_t a2, maskbits_t bits);
 int tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len);
 char *tor_dup_addr(uint32_t addr) ATTR_MALLOC;
+char *tor_dup_addr6(struct in6_addr addr) ATTR_MALLOC;
 int get_interface_address(int severity, uint32_t *addr);
 
 int get_interface_address6(int severity, sa_family_t family, tor_addr_t *addr);
Index: common/compat.c
===================================================================
--- common/compat.c	(Revision 12621)
+++ common/compat.c	(Arbeitskopie)
@@ -834,6 +834,16 @@
  * but works on Windows and Solaris.)
  */
 int
+tor_inet_aton6(const char *c, struct in6_addr* addr)
+{
+ return tor_inet_pton(AF_INET6, c, addr);
+}
+
+/** Set *addr to the IP address (in dotted-quad notation) stored in c.
+ * Return 1 on success, 0 if c is badly formatted.  (Like inet_aton(c,addr),
+ * but works on Windows and Solaris.)
+ */
+int
 tor_inet_aton(const char *c, struct in_addr* addr)
 {
 #ifdef HAVE_INET_ATON
@@ -1052,6 +1062,30 @@
  * doesn't treat raw IP addresses properly.)
  */
 int
+tor_lookup_hostname6(const char *name, struct in6_addr *addr)
+{
+  tor_addr_t myaddr;
+  int ret;
+
+  if ((ret = tor_addr_lookup(name, AF_INET6, &myaddr)))
+    return ret;
+
+  if (IN_FAMILY(&myaddr) == AF_INET6) {
+    *addr = myaddr.sa6.sin6_addr;
+    return ret;
+  }
+
+  return -1;
+}
+
+/** Similar behavior to Unix gethostbyname: resolve <b>name</b>, and set
+ * *<b>addr</b> to the proper IP address, in host byte order.  Returns 0
+ * on success, -1 on failure; 1 on transient failure.
+ *
+ * (This function exists because standard windows gethostbyname
+ * doesn't treat raw IP addresses properly.)
+ */
+int
 tor_lookup_hostname(const char *name, uint32_t *addr)
 {
   tor_addr_t myaddr;
@@ -1086,7 +1120,7 @@
   struct in6_addr iaddr6;
   tor_assert(name);
   tor_assert(addr);
-  tor_assert(family == AF_INET || family == AF_UNSPEC);
+  tor_assert(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC);
   memset(addr, 0, sizeof(addr)); /* Clear the extraneous fields. */
   if (!*name) {
     /* Empty address is an error. */
Index: common/compat.h
===================================================================
--- common/compat.h	(Revision 12621)
+++ common/compat.h	(Arbeitskopie)
@@ -389,9 +389,11 @@
 #define TOR_ADDR_BUF_LEN 46 /* ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255 */
 
 int tor_inet_aton(const char *cp, struct in_addr *addr) ATTR_NONNULL((1,2));
+int tor_inet_aton6(const char *cp, struct in6_addr *addr) ATTR_NONNULL((1,2));
 const char *tor_inet_ntop(int af, const void *src, char *dst, size_t len);
 int tor_inet_pton(int af, const char *src, void *dst);
 int tor_lookup_hostname(const char *name, uint32_t *addr) ATTR_NONNULL((1,2));
+int tor_lookup_hostname6(const char *name, struct in6_addr *addr) ATTR_NONNULL((1,2));
 void set_socket_nonblocking(int socket);
 int tor_socketpair(int family, int type, int protocol, int fd[2]);
 int network_init(void);

Attachment: signature.asc
Description: OpenPGP digital signature