Parcourir la source

Choose bridge addresses by IPv4/IPv6 preferences

teor (Tim Wilson-Brown) il y a 8 ans
Parent
commit
1648666203
4 fichiers modifiés avec 123 ajouts et 75 suppressions
  1. 107 69
      src/or/directory.c
  2. 2 2
      src/or/directory.h
  3. 11 2
      src/or/entrynodes.c
  4. 3 2
      src/or/router.c

+ 107 - 69
src/or/directory.c

@@ -497,11 +497,14 @@ MOCK_IMPL(void, directory_get_from_dirserver, (
       const node_t *node = choose_random_dirguard(type);
       if (node && node->ri) {
         /* every bridge has a routerinfo. */
-        tor_addr_t addr;
         routerinfo_t *ri = node->ri;
-        node_get_addr(node, &addr);
-        directory_initiate_command(&addr,
-                                   ri->or_port, 0/*no dirport*/,
+        /* clients always make OR connections to bridges */
+        tor_addr_port_t or_ap;
+        /* we are willing to use a non-preferred address if we need to */
+        fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0,
+                                             &or_ap);
+        directory_initiate_command(&or_ap.addr, or_ap.port,
+                                   NULL, 0, /*no dirport*/
                                    ri->cache_info.identity_digest,
                                    dir_purpose,
                                    router_purpose,
@@ -610,6 +613,80 @@ dirind_is_anon(dir_indirection_t ind)
   return ind == DIRIND_ANON_DIRPORT || ind == DIRIND_ANONYMOUS;
 }
 
+/* Choose reachable OR and Dir addresses and ports from status, copying them
+ * into use_or_ap and use_dir_ap. If indirection is anonymous, then we're
+ * connecting via another relay, so choose the primary IPv4 address and ports.
+ *
+ * status should have at least one reachable address, if we can't choose a
+ * reachable address, warn and return -1. Otherwise, return 0.
+ */
+static int
+directory_choose_address_routerstatus(const routerstatus_t *status,
+                                      dir_indirection_t indirection,
+                                      tor_addr_port_t *use_or_ap,
+                                      tor_addr_port_t *use_dir_ap)
+{
+  tor_assert(status != NULL);
+  tor_assert(use_or_ap != NULL);
+  tor_assert(use_dir_ap != NULL);
+
+  const int anonymized_connection = dirind_is_anon(indirection);
+  int have_or = 0, have_dir = 0;
+
+  /* We expect status to have at least one reachable address if we're
+   * connecting to it directly.
+   *
+   * Therefore, we can simply use the other address if the one we want isn't
+   * allowed by the firewall.
+   *
+   * (When Tor uploads and downloads a hidden service descriptor, it uses
+   * DIRIND_ANONYMOUS, except for Tor2Web, which uses DIRIND_ONEHOP.
+   * So this code will only modify the address for Tor2Web's HS descriptor
+   * fetches. Even Single Onion Servers (NYI) use DIRIND_ANONYMOUS, to avoid
+   * HSDirs denying service by rejecting descriptors.)
+   */
+
+  /* Initialise the OR / Dir addresses */
+  tor_addr_make_null(&use_or_ap->addr, AF_UNSPEC);
+  use_or_ap->port = 0;
+  tor_addr_make_null(&use_dir_ap->addr, AF_UNSPEC);
+  use_dir_ap->port = 0;
+
+  if (anonymized_connection) {
+    /* Use the primary (IPv4) OR address if we're making an indirect
+     * connection. */
+    tor_addr_from_ipv4h(&use_or_ap->addr, status->addr);
+    use_or_ap->port = status->or_port;
+    have_or = 1;
+  } else {
+    /* We use an IPv6 address if we have one and we prefer it.
+     * Use the preferred address and port if they are reachable, otherwise,
+     * use the alternate address and port (if any).
+     */
+    have_or = fascist_firewall_choose_address_rs(status,
+                                                 FIREWALL_OR_CONNECTION, 0,
+                                                 use_or_ap);
+  }
+
+  have_dir = fascist_firewall_choose_address_rs(status,
+                                                FIREWALL_DIR_CONNECTION, 0,
+                                                use_dir_ap);
+
+  /* We rejected both addresses. This isn't great. */
+  if (!have_or && !have_dir) {
+    log_warn(LD_BUG, "Rejected all OR and Dir addresses from %s when "
+             "launching a directory connection to: IPv4 %s OR %d Dir %d "
+             "IPv6 %s OR %d Dir %d", routerstatus_describe(status),
+             fmt_addr32(status->addr), status->or_port,
+             status->dir_port, fmt_addr(&status->ipv6_addr),
+             status->ipv6_orport, status->dir_port);
+    log_backtrace(LOG_WARN, LD_BUG, "Addresses came from");
+    return -1;
+  }
+
+  return 0;
+}
+
 /** Same as directory_initiate_command_routerstatus(), but accepts
  * rendezvous data to fetch a hidden service descriptor. */
 void
@@ -627,7 +704,8 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
   const node_t *node;
   tor_addr_port_t use_or_ap, use_dir_ap;
   const int anonymized_connection = dirind_is_anon(indirection);
-  int have_or = 0, have_dir = 0;
+
+  tor_assert(status != NULL);
 
   node = node_get_by_id(status->identity_digest);
 
@@ -654,57 +732,16 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
    * selection uses the preference in ClientPreferIPv6{OR,Dir}Port, if
    * possible. (If UseBridges is set, clients ignore all these settings.)
    *
-   * Now we use a similar process to select an address for the relay,
-   * but simply use the other address if the one we want isn't allowed by
-   * the firewall.
-   *
-   * (When Tor uploads and downloads a hidden service descriptor, it uses
-   * DIRIND_ANONYMOUS, except for Tor2Web, which uses DIRIND_ONEHOP.
-   * So this code will only modify the address for Tor2Web's HS descriptor
-   * fetches. Even Single Onion Servers (NYI) use DIRIND_ANONYMOUS, to avoid
-   * HSDirs denying service by rejecting descriptors.)
+   * Now choose an address that we can use to connect to the directory server.
    */
-
-  /* Initialise the OR / Dir addresses */
-  tor_addr_make_null(&use_or_ap.addr, AF_UNSPEC);
-  use_or_ap.port = 0;
-  tor_addr_make_null(&use_dir_ap.addr, AF_UNSPEC);
-  use_dir_ap.port = 0;
-
-  if (anonymized_connection) {
-    /* Use the primary (IPv4) OR address if we're making an indirect
-     * connection. */
-    tor_addr_from_ipv4h(&use_or_ap.addr, status->addr);
-    use_or_ap.port = status->or_port;
-    have_or = 1;
-  } else {
-    /* We use an IPv6 address if we have one and we prefer it.
-     * Use the preferred address and port if they are reachable, otherwise,
-     * use the alternate address and port (if any).
-     */
-    have_or = fascist_firewall_choose_address_rs(status,
-                                                 FIREWALL_OR_CONNECTION, 0,
-                                                 &use_or_ap);
-  }
-
-  have_dir = fascist_firewall_choose_address_rs(status,
-                                                FIREWALL_DIR_CONNECTION, 0,
-                                                &use_dir_ap);
-
-  /* We rejected both addresses. This isn't great. */
-  if (!have_or && !have_dir) {
-    log_warn(LD_BUG, "Rejected all OR and Dir addresses from %s when "
-             "launching a directory connection to: IPv4 %s OR %d Dir %d "
-             "IPv6 %s OR %d Dir %d", routerstatus_describe(status),
-             fmt_addr32(status->addr), status->or_port,
-             status->dir_port, fmt_addr(&status->ipv6_addr),
-             status->ipv6_orport, status->dir_port);
-    log_backtrace(LOG_WARN, LD_BUG, "Addresses came from");
+  if (directory_choose_address_routerstatus(status, indirection, &use_or_ap,
+                                            &use_dir_ap) < 0) {
     return;
   }
 
-  /* XX/teor - we don't retry the alternate OR/Dir address if this one fails.
-   * (See #6772.) Instead, we'll retry another directory on failure. */
+  /* We don't retry the alternate OR/Dir address for the same directory if
+   * the address we choose fails (#6772).
+   * Instead, we'll retry another directory on failure. */
 
   directory_initiate_command_rend(&use_or_ap, &use_dir_ap,
                                   status->identity_digest,
@@ -941,41 +978,42 @@ directory_command_should_use_begindir(const or_options_t *options,
 }
 
 /** Helper for directory_initiate_command_rend: send the
- * command to a server whose address is <b>_addr</b>, whose OR port is
- * <b>or_port</b>, whose directory port is <b>dir_port</b>, whose identity key
- * digest is <b>digest</b>, with purposes <b>dir_purpose</b> and
+ * command to a server whose OR address/port is <b>or_addr</b>/<b>or_port</b>,
+ * whose directory address/port is <b>dir_addr</b>/<b>dir_port</b>, whose
+ * identity key digest is <b>digest</b>, with purposes <b>dir_purpose</b> and
  * <b>router_purpose</b>, making an (in)direct connection as specified in
  * <b>indirection</b>, with command <b>resource</b>, <b>payload</b> of
  * <b>payload_len</b>, and asking for a result only <b>if_modified_since</b>.
  */
 void
-directory_initiate_command(const tor_addr_t *_addr,
-                           uint16_t or_port, uint16_t dir_port,
+directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port,
+                           const tor_addr_t *dir_addr, uint16_t dir_port,
                            const char *digest,
                            uint8_t dir_purpose, uint8_t router_purpose,
                            dir_indirection_t indirection, const char *resource,
                            const char *payload, size_t payload_len,
                            time_t if_modified_since)
 {
-  /* Assume _addr applies to both the ORPort and the DirPort, but use the
-   * null tor_addr if ORPort or DirPort are 0. */
   tor_addr_port_t or_ap, dir_ap;
 
-  if (or_port) {
-    tor_addr_copy(&or_ap.addr, _addr);
+  /* Use the null tor_addr and 0 port if the address or port isn't valid. */
+  if (tor_addr_port_is_valid(or_addr, or_port, 0)) {
+    tor_addr_copy(&or_ap.addr, or_addr);
+    or_ap.port = or_port;
   } else {
-    /* the family doesn't matter here, so make it the same as _addr */
-    tor_addr_make_null(&or_ap.addr, tor_addr_family(_addr));
+    /* the family doesn't matter here, so make it IPv4 */
+    tor_addr_make_null(&or_ap.addr, AF_INET);
+    or_port = 0;
   }
-  or_ap.port = or_port;
 
-  if (dir_port) {
-    tor_addr_copy(&dir_ap.addr, _addr);
+  if (tor_addr_port_is_valid(dir_addr, dir_port, 0)) {
+    tor_addr_copy(&dir_ap.addr, dir_addr);
+    dir_ap.port = dir_port;
   } else {
-    /* the family doesn't matter here, so make it the same as _addr */
-    tor_addr_make_null(&dir_ap.addr, tor_addr_family(_addr));
+    /* the family doesn't matter here, so make it IPv4 */
+    tor_addr_make_null(&dir_ap.addr, AF_INET);
+    dir_port = 0;
   }
-  dir_ap.port = dir_port;
 
   directory_initiate_command_rend(&or_ap, &dir_ap,
                              digest, dir_purpose,

+ 2 - 2
src/or/directory.h

@@ -66,8 +66,8 @@ int connection_dir_process_inbuf(dir_connection_t *conn);
 int connection_dir_finished_flushing(dir_connection_t *conn);
 int connection_dir_finished_connecting(dir_connection_t *conn);
 void connection_dir_about_to_close(dir_connection_t *dir_conn);
-void directory_initiate_command(const tor_addr_t *addr,
-                                uint16_t or_port, uint16_t dir_port,
+void directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port,
+                                const tor_addr_t *dir_addr, uint16_t dir_port,
                                 const char *digest,
                                 uint8_t dir_purpose, uint8_t router_purpose,
                                 dir_indirection_t indirection,

+ 11 - 2
src/or/entrynodes.c

@@ -2117,8 +2117,17 @@ launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge)
     return;
   }
 
-  directory_initiate_command(&bridge->addr,
-                             bridge->port, 0/*no dirport*/,
+  /* Until we get a descriptor for the bridge, we only know one address for
+   * it. If we  */
+  if (!fascist_firewall_allows_address_addr(&bridge->addr, bridge->port,
+                                            FIREWALL_OR_CONNECTION, 0)) {
+    log_notice(LD_CONFIG, "Tried to fetch a descriptor directly from a bridge, "
+               "but that bridge is not reachable through our firewall.");
+    return;
+  }
+
+  directory_initiate_command(&bridge->addr, bridge->port,
+                             NULL, 0, /*no dirport*/
                              bridge->identity,
                              DIR_PURPOSE_FETCH_SERVERDESC,
                              ROUTER_PURPOSE_BRIDGE,

+ 3 - 2
src/or/router.c

@@ -1244,14 +1244,15 @@ consider_testing_reachability(int test_or, int test_dir)
     extend_info_free(ei);
   }
 
+  /* XXX IPv6 self testing */
   tor_addr_from_ipv4h(&addr, me->addr);
   if (test_dir && !check_whether_dirport_reachable() &&
       !connection_get_by_type_addr_port_purpose(
                 CONN_TYPE_DIR, &addr, me->dir_port,
                 DIR_PURPOSE_FETCH_SERVERDESC)) {
     /* ask myself, via tor, for my server descriptor. */
-    directory_initiate_command(&addr,
-                               me->or_port, me->dir_port,
+    directory_initiate_command(&addr, me->or_port,
+                               &addr, me->dir_port,
                                me->cache_info.identity_digest,
                                DIR_PURPOSE_FETCH_SERVERDESC,
                                ROUTER_PURPOSE_GENERAL,