Browse Source

Detect and remove unreachable intro points

Robert Ransom 12 years ago
parent
commit
fbea8c8ef1
5 changed files with 54 additions and 0 deletions
  1. 8 0
      changes/bug3825a
  2. 16 0
      src/or/circuitlist.c
  3. 10 0
      src/or/or.h
  4. 19 0
      src/or/rendclient.c
  5. 1 0
      src/or/rendclient.h

+ 8 - 0
changes/bug3825a

@@ -0,0 +1,8 @@
+  o Major bugfixes:
+
+    - When one of a hidden service's introduction points appears to be
+      unreachable, stop trying it.  Previously, we would keep trying
+      to build circuits to the introduction point until we lost the
+      descriptor, usually because the user gave up and restarted Tor.
+      Partly fixes bug 3825.
+

+ 16 - 0
src/or/circuitlist.c

@@ -1121,6 +1121,9 @@ circuit_expire_all_dirty_circs(void)
  *     to note stats.
  *   - If purpose is C_INTRODUCE_ACK_WAIT, report the intro point
  *     failure we just had to the hidden service client module.
+ *   - If purpose is C_INTRODUCING and <b>reason</b> isn't TIMEOUT,
+ *     report to the hidden service client module that the intro point
+ *     we just tried may be unreachable.
  *   - Send appropriate destroys and edge_destroys for conns and
  *     streams attached to circ.
  *   - If circ->rend_splice is set (we are the midpoint of a joined
@@ -1203,6 +1206,19 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line,
                                            timed_out ?
                                            INTRO_POINT_FAILURE_TIMEOUT :
                                            INTRO_POINT_FAILURE_GENERIC);
+  } else if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCING &&
+             reason != END_STREAM_REASON_TIMEOUT) {
+    origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+    tor_assert(ocirc->build_state->chosen_exit);
+    tor_assert(ocirc->rend_data);
+    log_info(LD_REND, "Failed intro circ %s to %s "
+             "(building circuit to intro point). "
+             "Marking intro point as possibly unreachable.",
+             safe_str_client(ocirc->rend_data->onion_address),
+           safe_str_client(build_state_get_exit_nickname(ocirc->build_state)));
+    rend_client_report_intro_point_failure(ocirc->build_state->chosen_exit,
+                                           ocirc->rend_data,
+                                           INTRO_POINT_FAILURE_UNREACHABLE);
   }
   if (circ->n_conn) {
     circuit_clear_cell_queue(circ, circ->n_conn);

+ 10 - 0
src/or/or.h

@@ -3456,6 +3456,11 @@ typedef struct rend_encoded_v2_service_descriptor_t {
   char *desc_str; /**< Descriptor string. */
 } rend_encoded_v2_service_descriptor_t;
 
+/** The maximum number of non-circuit-build-timeout failures a hidden
+ * service client will tolerate while trying to build a circuit to an
+ * introduction point.  See also rend_intro_point_t.unreachable_count. */
+#define MAX_INTRO_POINT_REACHABILITY_FAILURES 5
+
 /** Introduction point information.  Used both in rend_service_t (on
  * the service side) and in rend_service_descriptor_t (on both the
  * client and service side). */
@@ -3470,6 +3475,11 @@ typedef struct rend_intro_point_t {
    * hidden service connection attempt, but it may be tried again
    * during a future connection attempt. */
   unsigned int timed_out : 1;
+
+  /** (Client side only) The number of times we have failed to build a
+   * circuit to this intro point for some reason other than our
+   * circuit-build timeout.  See also MAX_INTRO_POINT_REACHABILITY_FAILURES. */
+  unsigned int unreachable_count : 3;
 } rend_intro_point_t;
 
 /** Information used to connect to a hidden service.  Used on both the

+ 19 - 0
src/or/rendclient.c

@@ -662,6 +662,11 @@ rend_client_cancel_descriptor_fetches(void)
  * current hidden service connection attempt has ended or it has
  * appeared in a newly fetched rendezvous descriptor.
  *
+ * If <b>failure_type</b> is INTRO_POINT_FAILURE_UNREACHABLE,
+ * increment the intro point's reachability-failure count; if it has
+ * now failed MAX_INTRO_POINT_REACHABILITY_FAILURES or more times,
+ * remove the intro point from (our parsed copy of) the HS descriptor.
+ *
  * Return -1 if error, 0 if no usable intro points remain or service
  * unrecognized, 1 if recognized and some intro points remain.
  */
@@ -704,6 +709,20 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro,
       case INTRO_POINT_FAILURE_TIMEOUT:
         intro->timed_out = 1;
         break;
+      case INTRO_POINT_FAILURE_UNREACHABLE:
+        ++(intro->unreachable_count);
+        {
+          int zap_intro_point =
+            intro->unreachable_count >= MAX_INTRO_POINT_REACHABILITY_FAILURES;
+          log_info(LD_REND, "Failed to reach this intro point %u times.%s",
+                   intro->unreachable_count,
+                   zap_intro_point ? " Removing from descriptor.": "");
+          if (zap_intro_point) {
+            rend_intro_point_free(intro);
+            smartlist_del(ent->parsed->intro_nodes, i);
+          }
+        }
+        break;
       }
       break;
     }

+ 1 - 0
src/or/rendclient.h

@@ -25,6 +25,7 @@ void rend_client_purge_last_hid_serv_requests(void);
 
 #define INTRO_POINT_FAILURE_GENERIC 0
 #define INTRO_POINT_FAILURE_TIMEOUT 1
+#define INTRO_POINT_FAILURE_UNREACHABLE 2
 
 int rend_client_report_intro_point_failure(extend_info_t *failed_intro,
                                            const rend_data_t *rend_query,