Browse Source

Add reasons to DESTROY and RELAY_TRUNCATED cells.

svn:r5734
Nick Mathewson 18 years ago
parent
commit
878962bee1

+ 7 - 6
doc/TODO

@@ -43,9 +43,10 @@ N - if they're trying to be a tor server and they're running
       other nodes *are* reachable.
     o Make EntryNodes and StrictEntrynodes do what we want.
 
-N - Destroy and truncated cells should have reasons.
-    - Specify
-    - Implement
+N . Destroy and truncated cells should have reasons.
+    o Specify
+    o Implement
+    - Display the reasons under some circumstances?
 
 N . Only use a routerdesc if you recognize its hash.
     o (Must defer till dirservers are upgraded to latest code, which
@@ -85,9 +86,6 @@ N . Only use a routerdesc if you recognize its hash.
       - Test.
     - Non-directories don't need to keep descriptors in memory.
 
-N - Should router info have a pointer to routerstatus?
-    - We should at least do something about the duplicated fields.
-
 R - Christian Grothoff's attack of infinite-length circuit.
     the solution is to have a separate 'extend-data' cell type
     which is used for the first N data cells, and only
@@ -115,6 +113,9 @@ Deferred from 0.1.1.x:
     those, if circuits aren't working and it's a pattern we recognize
     ("port 443 worked once and port 9001 keeps not working").
 
+N - Should router info have a pointer to routerstatus?
+    - We should at least do something about the duplicated fields.
+
 N . Additional controller features
       o Find a way to make event info more extensible
       - change circuit status events to give more details, like purpose,

+ 24 - 1
doc/tor-spec.txt

@@ -136,7 +136,8 @@ when do we rotate which keys (tls, link, etc)?
       CREATE:  Payload contains the handshake challenge.
       CREATED: Payload contains the handshake response.
       RELAY:   Payload contains the relay header and relay body.
-      DESTROY: Payload is unused.
+      DESTROY: Payload contains a reason for closing the circuit.
+               (see 4.4)
    Upon receiving any other value for the command field, an OR must
    drop the cell.
 
@@ -376,6 +377,28 @@ when do we rotate which keys (tls, link, etc)?
    RELAY_TRUNCATED cell towards the OP; the node farther from the OP
    should send a DESTROY cell down the circuit.
 
+   The payload of a RELAY_TRUNCATED or DESTROY cell contains a single octet,
+   describing why the circuit is being closed or truncated.  When sending a
+   TRUNCATED or DESTROY cell because of another TRUNCATED or DESTROY cell,
+   the error code should be propagated.  The origin of a circuit always sets
+   this error code to 0, to avoid leaking its version.
+
+   The error codes are:
+     0 -- NONE            (No reason given.)
+     1 -- PROTOCOL        (Tor protocol violation.)
+     2 -- INTERNAL        (Internal error.)
+     3 -- REQUESTED       (A client sent a TRUNCATE command.)
+     4 -- HIBERNATING     (Not currently operating; trying to save bandwidth.)
+     5 -- RESOURCELIMIT (Out of memory, sockets, or circuit IDs.)
+     6 -- CONNECTFAILED (Unable to reach server.)
+     7 -- OR_IDENTITY     (Connected to server, but its OR identity was not
+                           as expected.)
+     8 -- OR_CONN_CLOSED  (The OR connection that was carrying this circuit
+                           died.)
+
+   [Versions of Tor prior to 0.1.0.11 didn't sent versions; implementations
+   MUST accept empty TRUNCATED and DESTROY cells.]
+
 4.5. Routing relay cells
 
    When an OR receives a RELAY cell, it checks the cell's circID and

+ 20 - 19
src/or/circuitbuild.c

@@ -313,14 +313,14 @@ circuit_establish_circuit(uint8_t purpose, extend_info_t *info,
 
   if (onion_pick_cpath_exit(circ, info) < 0 ||
       onion_populate_cpath(circ) < 0) {
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
     return NULL;
   }
 
   control_event_circuit_status(circ, CIRC_EVENT_LAUNCHED);
 
   if (circuit_handle_first_hop(circ) < 0) {
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
     return NULL;
   }
   return circ;
@@ -420,7 +420,7 @@ circuit_n_conn_done(connection_t *or_conn, int status)
                 DIGEST_LEN)) {
       if (!status) { /* or_conn failed; close circ */
         info(LD_CIRC,"or_conn failed. Closing circ.");
-        circuit_mark_for_close(circ);
+        circuit_mark_for_close(circ, END_CIRC_REASON_OR_IDENTITY);
         continue;
       }
       debug(LD_CIRC,"Found circ %d, sending create cell.", circ->n_circ_id);
@@ -432,7 +432,7 @@ circuit_n_conn_done(connection_t *or_conn, int status)
         if (circuit_send_next_onion_skin(circ) < 0) {
           info(LD_CIRC,
                "send_next_onion_skin failed; circuit marked for closing.");
-          circuit_mark_for_close(circ);
+          circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
           continue;
           /* XXX could this be bad, eg if next_onion_skin failed because conn
            *     died? */
@@ -441,7 +441,7 @@ circuit_n_conn_done(connection_t *or_conn, int status)
         /* pull the create cell out of circ->onionskin, and send it */
         tor_assert(circ->onionskin);
         if (circuit_deliver_create_cell(circ,CELL_CREATE,circ->onionskin)<0) {
-          circuit_mark_for_close(circ);
+          circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
           continue;
         }
         tor_free(circ->onionskin);
@@ -537,7 +537,7 @@ extern int has_completed_circuit;
  * Otherwise, we need to build a relay extend cell and send it
  * forward.
  *
- * Return -1 if we want to tear down circ, else return 0.
+ * Return -reason if we want to tear down circ, else return 0.
  */
 int
 circuit_send_next_onion_skin(circuit_t *circ)
@@ -567,7 +567,7 @@ circuit_send_next_onion_skin(circuit_t *circ)
                             &(circ->cpath->dh_handshake_state),
                             payload) < 0) {
         warn(LD_CIRC,"onion_skin_create (first hop) failed.");
-        return -1;
+        return - END_CIRC_REASON_INTERNAL;
       }
     } else {
       /* We are not an OR, and we're building the first hop of a circuit to a
@@ -582,7 +582,7 @@ circuit_send_next_onion_skin(circuit_t *circ)
     }
 
     if (circuit_deliver_create_cell(circ, cell_type, payload) < 0)
-      return -1;
+      return - END_CIRC_REASON_RESOURCELIMIT;
 
     circ->cpath->state = CPATH_STATE_AWAITING_KEYS;
     circuit_set_state(circ, CIRCUIT_STATE_BUILDING);
@@ -625,7 +625,7 @@ circuit_send_next_onion_skin(circuit_t *circ)
     if (onion_skin_create(hop->extend_info->onion_key,
                           &(hop->dh_handshake_state), onionskin) < 0) {
       warn(LD_CIRC,"onion_skin_create failed.");
-      return -1;
+      return - END_CIRC_REASON_INTERNAL;
     }
 
     debug(LD_CIRC,"Sending extend relay cell.");
@@ -719,7 +719,7 @@ circuit_extend(cell_t *cell, circuit_t *circ)
       n_conn = connection_or_connect(circ->n_addr, circ->n_port, id_digest);
       if (!n_conn) {
         info(LD_CIRC,"Launching n_conn failed. Closing circuit.");
-        circuit_mark_for_close(circ);
+        circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED);
         return 0;
       }
       debug(LD_CIRC,"connecting in progress (or finished). Good.");
@@ -801,7 +801,7 @@ circuit_init_cpath_crypto(crypt_path_t *cpath, char *key_data, int reverse)
  * Calculate the appropriate keys and digests, make sure KH is
  * correct, and initialize this hop of the cpath.
  *
- * Return -1 if we want to mark circ for close, else return 0.
+ * Return - reason if we want to mark circ for close, else return 0.
  */
 int
 circuit_finish_handshake(circuit_t *circ, uint8_t reply_type, char *reply)
@@ -816,7 +816,7 @@ circuit_finish_handshake(circuit_t *circ, uint8_t reply_type, char *reply)
     hop = onion_next_hop_in_cpath(circ->cpath);
     if (!hop) { /* got an extended when we're all done? */
       warn(LD_PROTOCOL,"got extended when circ already built? Closing.");
-      return -1;
+      return - END_CIRC_REASON_TORPROTOCOL;
     }
   }
   tor_assert(hop->state == CPATH_STATE_AWAITING_KEYS);
@@ -825,7 +825,7 @@ circuit_finish_handshake(circuit_t *circ, uint8_t reply_type, char *reply)
     if (onion_skin_client_handshake(hop->dh_handshake_state, reply, keys,
                                     DIGEST_LEN*2+CIPHER_KEY_LEN*2) < 0) {
       warn(LD_CIRC,"onion_skin_client_handshake failed.");
-      return -1;
+      return -END_CIRC_REASON_TORPROTOCOL;
     }
     /* Remember hash of g^xy */
     memcpy(hop->handshake_digest, reply+DH_KEY_LEN, DIGEST_LEN);
@@ -833,12 +833,12 @@ circuit_finish_handshake(circuit_t *circ, uint8_t reply_type, char *reply)
     if (fast_client_handshake(hop->fast_handshake_state, reply, keys,
                               DIGEST_LEN*2+CIPHER_KEY_LEN*2) < 0) {
       warn(LD_CIRC,"fast_client_handshake failed.");
-      return -1;
+      return -END_CIRC_REASON_TORPROTOCOL;
     }
     memcpy(hop->handshake_digest, reply+DIGEST_LEN, DIGEST_LEN);
   } else {
     warn(LD_PROTOCOL,"CREATED cell type did not match CREATE cell type.");
-    return -1;
+    return -END_CIRC_REASON_TORPROTOCOL;
   }
 
   if (hop->dh_handshake_state) {
@@ -848,7 +848,7 @@ circuit_finish_handshake(circuit_t *circ, uint8_t reply_type, char *reply)
   memset(hop->fast_handshake_state, 0, sizeof(hop->fast_handshake_state));
 
   if (circuit_init_cpath_crypto(hop, keys, 0)<0) {
-    return -1;
+    return -END_CIRC_REASON_TORPROTOCOL;
   }
 
   hop->state = CPATH_STATE_OPEN;
@@ -880,7 +880,7 @@ circuit_truncated(circuit_t *circ, crypt_path_t *layer)
    *     means that a connection broke or an extend failed. For now,
    *     just give up.
    */
-  circuit_mark_for_close(circ);
+  circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
   return 0;
 
 #if 0
@@ -1375,12 +1375,13 @@ circuit_append_new_exit(circuit_t *circ, extend_info_t *info)
 int
 circuit_extend_to_new_exit(circuit_t *circ, extend_info_t *info)
 {
+  tor_assert(CIRCUIT_IS_ORIGIN(circ));
   circuit_append_new_exit(circ, info);
   circuit_set_state(circ, CIRCUIT_STATE_BUILDING);
   if (circuit_send_next_onion_skin(circ)<0) {
     warn(LD_CIRC, "Couldn't extend circuit to new point '%s'.",
          info->nickname);
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
     return -1;
   }
   return 0;
@@ -1731,7 +1732,7 @@ static INLINE int
 is_an_entry_node(char *digest)
 {
   SMARTLIST_FOREACH(entry_nodes, entry_node_t *, entry,
-                    if(!memcmp(digest, entry->identity, DIGEST_LEN))
+                    if (!memcmp(digest, entry->identity, DIGEST_LEN))
                       return 1;
                    );
   return 0;

+ 24 - 7
src/or/circuitlist.c

@@ -470,7 +470,7 @@ circuit_get_by_edge_conn(connection_t *conn)
  * been marked already.
  */
 void
-circuit_unlink_all_from_or_conn(connection_t *conn)
+circuit_unlink_all_from_or_conn(connection_t *conn, int reason)
 {
   circuit_t *circ;
   for (circ = global_circuitlist; circ; circ = circ->next) {
@@ -480,7 +480,7 @@ circuit_unlink_all_from_or_conn(connection_t *conn)
       if (circ->p_conn == conn)
         circuit_set_circid_orconn(circ, 0, NULL, P_CONN_CHANGED);
       if (!circ->marked_for_close)
-        circuit_mark_for_close(circ);
+        circuit_mark_for_close(circ, reason);
     }
   }
 }
@@ -607,7 +607,7 @@ circuit_mark_all_unused_circs(void)
     if (CIRCUIT_IS_ORIGIN(circ) &&
         !circ->marked_for_close &&
         !circ->timestamp_dirty)
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
   }
 }
 
@@ -648,7 +648,8 @@ circuit_expire_all_dirty_circs(void)
  *     rendezvous stream), then mark the other circuit to close as well.
  */
 void
-_circuit_mark_for_close(circuit_t *circ, int line, const char *file)
+_circuit_mark_for_close(circuit_t *circ, int reason, int line,
+                        const char *file)
 {
   connection_t *conn;
 
@@ -663,6 +664,22 @@ _circuit_mark_for_close(circuit_t *circ, int line, const char *file)
         circ->marked_for_close_file, circ->marked_for_close);
     return;
   }
+  if (reason == END_CIRC_AT_ORIGIN) {
+    if (!CIRCUIT_IS_ORIGIN(circ)) {
+      warn(LD_BUG, "Specified 'at-origin' non-reason for ending circuit, "
+           "but circuit was not at origin. (called %s:%d, purpose=%d)",
+           file, line, circ->purpose);
+    }
+    reason = END_CIRC_REASON_NONE;
+  } else if (CIRCUIT_IS_ORIGIN(circ) && reason != END_CIRC_REASON_NONE) {
+    /* Don't warn about this; there are plenty of places where our code
+     * is origin-agnosic. */
+    reason = END_CIRC_REASON_NONE;
+  }
+  if (reason < _END_CIRC_REASON_MIN || reason > _END_CIRC_REASON_MAX) {
+    warn(LD_BUG, "Reason %d out of range at %s:%d", reason, file, line);
+    reason = END_CIRC_REASON_NONE;
+  }
 
   if (circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) {
     onion_pending_remove(circ);
@@ -698,7 +715,7 @@ _circuit_mark_for_close(circuit_t *circ, int line, const char *file)
   }
 
   if (circ->n_conn)
-    connection_send_destroy(circ->n_circ_id, circ->n_conn);
+    connection_or_send_destroy(circ->n_circ_id, circ->n_conn, reason);
   for (conn=circ->n_streams; conn; conn=conn->next_stream)
     connection_edge_destroy(circ->n_circ_id, conn);
   while (circ->resolving_streams) {
@@ -714,7 +731,7 @@ _circuit_mark_for_close(circuit_t *circ, int line, const char *file)
     conn->on_circuit = NULL;
   }
   if (circ->p_conn)
-    connection_send_destroy(circ->p_circ_id, circ->p_conn);
+    connection_or_send_destroy(circ->p_circ_id, circ->p_conn, reason);
   for (conn=circ->p_streams; conn; conn=conn->next_stream)
     connection_edge_destroy(circ->p_circ_id, conn);
 
@@ -724,7 +741,7 @@ _circuit_mark_for_close(circuit_t *circ, int line, const char *file)
   if (circ->rend_splice) {
     if (!circ->rend_splice->marked_for_close) {
       /* do this after marking this circuit, to avoid infinite recursion. */
-      circuit_mark_for_close(circ->rend_splice);
+      circuit_mark_for_close(circ->rend_splice, reason);
     }
     circ->rend_splice = NULL;
   }

+ 5 - 5
src/or/circuituse.c

@@ -264,7 +264,7 @@ circuit_expire_building(time_t now)
            circuit_state_to_string(victim->state), victim->purpose);
 
     circuit_log_path(LOG_INFO,LD_CIRC,victim);
-    circuit_mark_for_close(victim);
+    circuit_mark_for_close(victim, END_CIRC_AT_ORIGIN);
   }
 }
 
@@ -523,7 +523,7 @@ circuit_about_to_close_connection(connection_t *conn)
       /* Inform any pending (not attached) circs that they should give up. */
       circuit_n_conn_done(conn, 0);
       /* Now close all the attached circuits on it. */
-      circuit_unlink_all_from_or_conn(conn);
+      circuit_unlink_all_from_or_conn(conn, END_CIRC_REASON_OR_CONN_CLOSED);
       return;
     }
     case CONN_TYPE_AP:
@@ -568,7 +568,7 @@ circuit_expire_old_circuits(void)
       /* (only general and purpose_c circs can get dirty) */
       tor_assert(!circ->n_streams);
       tor_assert(circ->purpose <= CIRCUIT_PURPOSE_C_REND_JOINED);
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
     } else if (!circ->timestamp_dirty && CIRCUIT_IS_ORIGIN(circ) &&
                circ->state == CIRCUIT_STATE_OPEN &&
                circ->purpose == CIRCUIT_PURPOSE_C_GENERAL) {
@@ -576,7 +576,7 @@ circuit_expire_old_circuits(void)
       if (circ->timestamp_created + CIRCUIT_UNUSED_CIRC_TIMEOUT < now) {
         debug(LD_CIRC,"Closing circuit that has been unused for %d seconds.",
               (int)(now - circ->timestamp_created));
-        circuit_mark_for_close(circ);
+        circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
       }
     }
   }
@@ -589,7 +589,7 @@ circuit_testing_opened(circuit_t *circ)
   /* For now, we only use testing circuits to see if our ORPort is
      reachable. But we remember reachability in onionskin_answer(),
      so there's no need to record anything here. Just close the circ. */
-  circuit_mark_for_close(circ);
+  circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
 }
 
 /** A testing circuit has failed to build. Take whatever stats we want. */

+ 24 - 16
src/or/command.c

@@ -166,7 +166,8 @@ command_process_create_cell(cell_t *cell, connection_t *conn)
   if (we_are_hibernating()) {
     info(LD_OR,"Received create cell but we're shutting down. Sending back "
          "destroy.");
-    connection_send_destroy(cell->circ_id, conn);
+    connection_or_send_destroy(cell->circ_id, conn,
+                               END_CIRC_REASON_HIBERNATING);
     return;
   }
 
@@ -214,7 +215,7 @@ command_process_create_cell(cell_t *cell, connection_t *conn)
     /* hand it off to the cpuworkers, and then return */
     if (assign_to_cpuworker(NULL, CPUWORKER_TASK_ONION, circ) < 0) {
       warn(LD_GENERAL,"Failed to hand off onionskin. Closing.");
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
       return;
     }
     debug(LD_OR,"success: handed off onionskin.");
@@ -226,12 +227,12 @@ command_process_create_cell(cell_t *cell, connection_t *conn)
     tor_assert(cell->command == CELL_CREATE_FAST);
     if (fast_server_handshake(cell->payload, reply, keys, sizeof(keys))<0) {
       warn(LD_OR,"Failed to generate key material. Closing.");
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
       return;
     }
     if (onionskin_answer(circ, CELL_CREATED_FAST, reply, keys)<0) {
       warn(LD_OR,"Failed to reply to CREATE_FAST cell. Closing.");
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
       return;
     }
   }
@@ -261,7 +262,7 @@ command_process_created_cell(cell_t *cell, connection_t *conn)
   if (circ->n_circ_id != cell->circ_id) {
     log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,
            "got created cell from OPward? Closing.");
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL);
     return;
   }
 
@@ -269,13 +270,14 @@ command_process_created_cell(cell_t *cell, connection_t *conn)
     debug(LD_OR,"at OP. Finishing handshake.");
     if (circuit_finish_handshake(circ, cell->command, cell->payload) < 0) {
       warn(LD_OR,"circuit_finish_handshake failed.");
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
       return;
     }
     debug(LD_OR,"Moving to next skin.");
     if (circuit_send_next_onion_skin(circ) < 0) {
       info(LD_OR,"circuit_send_next_onion_skin failed.");
-      circuit_mark_for_close(circ); /* XXX push this circuit_close lower */
+      /* XXX push this circuit_close lower */
+      circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
       return;
     }
   } else { /* pack it into an extended relay cell, and send it. */
@@ -293,6 +295,7 @@ static void
 command_process_relay_cell(cell_t *cell, connection_t *conn)
 {
   circuit_t *circ;
+  int reason;
 
   circ = circuit_get_by_circid_orconn(cell->circ_id, conn);
 
@@ -304,22 +307,24 @@ command_process_relay_cell(cell_t *cell, connection_t *conn)
 
   if (circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) {
     log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,"circuit in create_wait. Closing.");
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL);
     return;
   }
 
   if (cell->circ_id == circ->p_circ_id) { /* it's an outgoing cell */
-    if (circuit_receive_relay_cell(cell, circ, CELL_DIRECTION_OUT) < 0) {
+    if ((reason = circuit_receive_relay_cell(cell, circ,
+                                             CELL_DIRECTION_OUT)) < 0) {
       log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,"circuit_receive_relay_cell "
              "(forward) failed. Closing.");
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, -reason);
       return;
     }
   } else { /* it's an ingoing cell */
-    if (circuit_receive_relay_cell(cell, circ, CELL_DIRECTION_IN) < 0) {
+    if ((reason = circuit_receive_relay_cell(cell, circ,
+                                             CELL_DIRECTION_IN)) < 0) {
       log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,"circuit_receive_relay_cell "
              "(backward) failed. Closing.");
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, -reason);
       return;
     }
   }
@@ -342,9 +347,10 @@ static void
 command_process_destroy_cell(cell_t *cell, connection_t *conn)
 {
   circuit_t *circ;
+  uint8_t reason;
 
   circ = circuit_get_by_circid_orconn(cell->circ_id, conn);
-
+  reason = (uint8_t)cell->payload[0];
   if (!circ) {
     info(LD_OR,"unknown circuit %d on connection from %s:%d. Dropping.",
          cell->circ_id, conn->address, conn->port);
@@ -355,15 +361,17 @@ command_process_destroy_cell(cell_t *cell, connection_t *conn)
   if (cell->circ_id == circ->p_circ_id) {
     /* the destroy came from behind */
     circuit_set_circid_orconn(circ, 0, NULL, P_CONN_CHANGED);
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, reason);
   } else { /* the destroy came from ahead */
     circuit_set_circid_orconn(circ, 0, NULL, N_CONN_CHANGED);
     if (CIRCUIT_IS_ORIGIN(circ)) {
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, reason);
     } else {
+      char payload[1];
       debug(LD_OR, "Delivering 'truncated' back.");
+      payload[0] = (char)reason;
       connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED,
-                                   NULL, 0, NULL);
+                                   payload, sizeof(payload), NULL);
     }
   }
 }

+ 9 - 26
src/or/connection.c

@@ -1038,7 +1038,8 @@ connection_read_bucket_decrement(connection_t *conn, int num_read)
   }
 }
 
-/** DOCDOC */
+/** If we have exhaused our global read bucket, or the read bucket for conn,
+ * stop reading. */
 static void
 connection_consider_empty_buckets(connection_t *conn)
 {
@@ -1546,7 +1547,8 @@ connection_write_to_buf(const char *string, size_t len, connection_t *conn)
       /* if it failed, it means we have our package/delivery windows set
          wrong compared to our max outbuf size. close the whole circuit. */
       warn(LD_NET,"write_to_buf failed. Closing circuit (fd %d).", conn->s);
-      circuit_mark_for_close(circuit_get_by_edge_conn(conn));
+      circuit_mark_for_close(circuit_get_by_edge_conn(conn),
+                             END_CIRC_REASON_INTERNAL);
     } else {
       warn(LD_NET,"write_to_buf failed. Closing connection (fd %d).", conn->s);
       connection_mark_for_close(conn);
@@ -1784,28 +1786,6 @@ connection_state_is_connecting(connection_t *conn)
   return 0;
 }
 
-/** Write a destroy cell with circ ID <b>circ_id</b> onto OR connection
- * <b>conn</b>.
- *
- * Return 0.
- */
-/*XXXX Why isn't this in connection_or.c?*/
-int
-connection_send_destroy(uint16_t circ_id, connection_t *conn)
-{
-  cell_t cell;
-
-  tor_assert(conn);
-  tor_assert(connection_speaks_cells(conn));
-
-  memset(&cell, 0, sizeof(cell_t));
-  cell.circ_id = circ_id;
-  cell.command = CELL_DESTROY;
-  debug(LD_OR,"Sending destroy (circID %d).", circ_id);
-  connection_or_write_cell_to_buf(&cell, conn);
-  return 0;
-}
-
 /** Alloocates a base64'ed authenticator for use in http or https
  * auth, based on the input string <b>authenticator</b>. Returns it
  * if success, else returns NULL. */
@@ -1829,9 +1809,12 @@ alloc_http_authenticator(const char *authenticator)
   return base64_authenticator;
 }
 
-/** DOCDOC
- * XXXX ipv6 NM
+/** Given a socket handle, check whether the local address (sockname) of the
+ * socket is one that we've connected from before.  If so, double-check
+ * whether our address has changed and we need to generate keys.  If we do,
+ * call init_keys().
  */
+/* XXXX Handle IPv6, eventually. */
 static void
 client_check_address_changed(int sock)
 {

+ 4 - 4
src/or/connection_edge.c

@@ -1208,7 +1208,7 @@ connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ)
   ap_conn->stream_id = get_unique_stream_id_by_circ(circ);
   if (ap_conn->stream_id==0) {
     connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL);
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
     return -1;
   }
 
@@ -1254,7 +1254,7 @@ connection_ap_handshake_send_resolve(connection_t *ap_conn, circuit_t *circ)
   ap_conn->stream_id = get_unique_stream_id_by_circ(circ);
   if (ap_conn->stream_id==0) {
     connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL);
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
     return -1;
   }
 
@@ -1519,8 +1519,8 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
       connection_edge_end(n_stream, END_STREAM_REASON_EXITPOLICY,
                           n_stream->cpath_layer);
       connection_free(n_stream);
-      circuit_mark_for_close(circ); /* knock the whole thing down, somebody
-                                     * screwed up */
+      /* knock the whole thing down, somebody screwed up */
+      circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED);
       tor_free(address);
       return 0;
     }

+ 23 - 0
src/or/connection_or.c

@@ -779,3 +779,26 @@ loop:
   goto loop; /* process the remainder of the buffer */
 }
 
+/** Write a destroy cell with circ ID <b>circ_id</b> and reason <b>reason</b>
+ * onto OR connection <b>conn</b>.  Don't perform range-checking on reason:
+ * we may want to propagate reasons from other cells.
+ *
+ * Return 0.
+ */
+int
+connection_or_send_destroy(uint16_t circ_id, connection_t *conn, int reason)
+{
+  cell_t cell;
+
+  tor_assert(conn);
+  tor_assert(connection_speaks_cells(conn));
+
+  memset(&cell, 0, sizeof(cell_t));
+  cell.circ_id = circ_id;
+  cell.command = CELL_DESTROY;
+  cell.payload[0] = (uint8_t) reason;
+  debug(LD_OR,"Sending destroy (circID %d).", circ_id);
+  connection_or_write_cell_to_buf(&cell, conn);
+  return 0;
+}
+

+ 3 - 3
src/or/control.c

@@ -1611,7 +1611,7 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len,
   /* now that we've populated the cpath, start extending */
   if (zero_circ) {
     if (circuit_handle_first_hop(circ) < 0) {
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
       if (v0)
         send_control0_error(conn, ERR_INTERNAL, "couldn't start circuit");
       else
@@ -1624,7 +1624,7 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len,
       if (circuit_send_next_onion_skin(circ) < 0) {
         info(LD_CONTROL,
              "send_next_onion_skin failed; circuit marked for closing.");
-        circuit_mark_for_close(circ);
+        circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
         if (v0)
           send_control0_error(conn, ERR_INTERNAL, "couldn't send onion skin");
         else
@@ -1967,7 +1967,7 @@ handle_control_closecircuit(connection_t *conn, uint32_t len,
   }
 
   if (!safe || !circ->p_streams) {
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_REASON_NONE);
   }
 
   send_control_done(conn);

+ 2 - 2
src/or/cpuworker.c

@@ -169,7 +169,7 @@ connection_cpu_process_inbuf(connection_t *conn)
       debug(LD_OR,
             "decoding onionskin failed. (Old key or bad software.) Closing.");
       if (circ)
-        circuit_mark_for_close(circ);
+        circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL);
       goto done_processing;
     }
     if (!circ) {
@@ -185,7 +185,7 @@ connection_cpu_process_inbuf(connection_t *conn)
     if (onionskin_answer(circ, CELL_CREATED, buf+TAG_LEN,
                          buf+TAG_LEN+ONIONSKIN_REPLY_LEN) < 0) {
       warn(LD_OR,"onionskin_answer failed. Closing.");
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
       goto done_processing;
     }
     debug(LD_OR,"onionskin_answer succeeded. Yay.");

+ 1 - 1
src/or/onion.c

@@ -71,7 +71,7 @@ onion_pending_add(circuit_t *circ)
     onion_pending_remove(ol_list->circ);
     info(LD_CIRC,
          "Circuit create request is too old; cancelling due to overload.");
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
   }
   return 0;
 }

+ 20 - 5
src/or/or.h

@@ -481,6 +481,19 @@ typedef enum {
 #define RESOLVED_TYPE_ERROR_TRANSIENT 0xF0
 #define RESOLVED_TYPE_ERROR 0xF1
 
+#define END_CIRC_AT_ORIGIN           -1
+#define _END_CIRC_REASON_MIN            0
+#define END_CIRC_REASON_NONE            0
+#define END_CIRC_REASON_TORPROTOCOL     1
+#define END_CIRC_REASON_INTERNAL        2
+#define END_CIRC_REASON_REQUESTED       3
+#define END_CIRC_REASON_HIBERNATING     4
+#define END_CIRC_REASON_RESOURCELIMIT   5
+#define END_CIRC_REASON_CONNECTFAILED   6
+#define END_CIRC_REASON_OR_IDENTITY     7
+#define END_CIRC_REASON_OR_CONN_CLOSED  8
+#define _END_CIRC_REASON_MAX            8
+
 /** Length of 'y' portion of 'y.onion' URL. */
 #define REND_SERVICE_ID_LEN 16
 
@@ -1494,7 +1507,7 @@ circuit_t *circuit_new(uint16_t p_circ_id, connection_t *p_conn);
 circuit_t *circuit_get_by_circid_orconn(uint16_t circ_id, connection_t *conn);
 int circuit_id_used_on_conn(uint16_t circ_id, connection_t *conn);
 circuit_t *circuit_get_by_edge_conn(connection_t *conn);
-void circuit_unlink_all_from_or_conn(connection_t *conn);
+void circuit_unlink_all_from_or_conn(connection_t *conn, int reason);
 circuit_t *circuit_get_by_global_id(uint32_t id);
 circuit_t *circuit_get_by_rend_query_and_purpose(const char *rend_query,
                                                  uint8_t purpose);
@@ -1506,10 +1519,11 @@ circuit_t *circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
                                        int need_capacity, int internal);
 void circuit_mark_all_unused_circs(void);
 void circuit_expire_all_dirty_circs(void);
-void _circuit_mark_for_close(circuit_t *circ, int line, const char *file);
+void _circuit_mark_for_close(circuit_t *circ, int reason,
+                             int line, const char *file);
 
-#define circuit_mark_for_close(c) \
-  _circuit_mark_for_close((c), __LINE__, _SHORT_FILE_)
+#define circuit_mark_for_close(c, reason)                               \
+  _circuit_mark_for_close((c), (reason), __LINE__, _SHORT_FILE_)
 
 void assert_cpath_layer_ok(const crypt_path_t *cp);
 void assert_circuit_ok(const circuit_t *c);
@@ -1651,7 +1665,6 @@ int connection_is_listener(connection_t *conn);
 int connection_state_is_open(connection_t *conn);
 int connection_state_is_connecting(connection_t *conn);
 
-int connection_send_destroy(uint16_t circ_id, connection_t *conn);
 char *alloc_http_authenticator(const char *authenticator);
 
 void assert_connection_ok(connection_t *conn, time_t now);
@@ -1742,6 +1755,8 @@ int connection_tls_start_handshake(connection_t *conn, int receiving);
 int connection_tls_continue_handshake(connection_t *conn);
 
 void connection_or_write_cell_to_buf(const cell_t *cell, connection_t *conn);
+int connection_or_send_destroy(uint16_t circ_id, connection_t *conn,
+                               int reason);
 
 /********************************* control.c ***************************/
 

+ 36 - 26
src/or/relay.c

@@ -138,6 +138,8 @@ relay_crypt_one_payload(crypto_cipher_env_t *cipher, char *in,
  *    connection_edge.
  *  - Else connection_or_write_cell_to_buf to the conn on the other
  *    side of the circuit.
+ *
+ * Return -reason on failure.
  */
 int
 circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, int cell_direction)
@@ -145,6 +147,7 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, int cell_direction)
   connection_t *conn=NULL;
   crypt_path_t *layer_hint=NULL;
   char recognized=0;
+  int reason;
 
   tor_assert(cell);
   tor_assert(circ);
@@ -163,20 +166,21 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, int cell_direction)
     if (cell_direction == CELL_DIRECTION_OUT) {
       ++stats_n_relay_cells_delivered;
       debug(LD_OR,"Sending away from origin.");
-      if (connection_edge_process_relay_cell(cell, circ, conn, NULL) < 0) {
+      if ((reason=connection_edge_process_relay_cell(cell, circ, conn, NULL))
+          < 0) {
         log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
                "connection_edge_process_relay_cell (away from origin) "
                "failed.");
-        return -1;
+        return reason;
       }
     }
     if (cell_direction == CELL_DIRECTION_IN) {
       ++stats_n_relay_cells_delivered;
       debug(LD_OR,"Sending to origin.");
-      if (connection_edge_process_relay_cell(cell, circ, conn,
-                                             layer_hint) < 0) {
+      if ((reason = connection_edge_process_relay_cell(cell, circ, conn,
+                                                       layer_hint)) < 0) {
         warn(LD_OR,"connection_edge_process_relay_cell (at origin) failed.");
-        return -1;
+        return reason;
       }
     }
     return 0;
@@ -197,19 +201,19 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, int cell_direction)
       tor_assert(circ->rend_splice->purpose ==
                  CIRCUIT_PURPOSE_REND_ESTABLISHED);
       cell->circ_id = circ->rend_splice->p_circ_id;
-      if (circuit_receive_relay_cell(cell, circ->rend_splice,
-                                     CELL_DIRECTION_IN) < 0) {
+      if ((reason = circuit_receive_relay_cell(cell, circ->rend_splice,
+                                               CELL_DIRECTION_IN)) < 0) {
         warn(LD_REND, "Error relaying cell across rendezvous; closing "
              "circuits");
         /* XXXX Do this here, or just return -1? */
-        circuit_mark_for_close(circ);
-        return -1;
+        circuit_mark_for_close(circ, -reason);
+        return reason;
       }
       return 0;
     }
     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
            "Didn't recognize cell, but circ stops here! Closing circ.");
-    return -1;
+    return -END_CIRC_REASON_TORPROTOCOL;
   }
 
   debug(LD_OR,"Passing on unrecognized cell.");
@@ -489,7 +493,7 @@ connection_edge_send_command(connection_t *fromconn, circuit_t *circ,
   if (circuit_package_relay_cell(&cell, circ, cell_direction, cpath_layer)
       < 0) {
     warn(LD_BUG,"circuit_package_relay_cell failed. Closing.");
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
     return -1;
   }
   return 0;
@@ -655,7 +659,7 @@ connection_edge_process_end_not_open(
       warn(LD_PROTOCOL,
            "Got an end because of %s, but we're not an AP. Closing.",
            connection_edge_end_reason_str(reason));
-      return -1;
+      return - END_CIRC_REASON_TORPROTOCOL;
     }
     info(LD_APP,"Address '%s' refused due to '%s'. Considering retrying.",
          safe_str(conn->socks_request->address),
@@ -848,7 +852,7 @@ connection_edge_process_relay_cell_not_open(
  * If <b>layer_hint</b> is defined, then we're the origin of the
  * circuit, and it specifies the hop that packaged <b>cell</b>.
  *
- * Return -1 if you want to warn and tear down the circuit, else 0.
+ * Return -reason if you want to warn and tear down the circuit, else 0.
  */
 static int
 connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
@@ -858,6 +862,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
   static int num_seen=0;
   relay_header_t rh;
   unsigned domain = layer_hint?LD_APP:LD_EXIT;
+  int reason;
 
   tor_assert(cell);
   tor_assert(circ);
@@ -869,7 +874,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
 
   if (rh.length > RELAY_PAYLOAD_SIZE) {
     warn(LD_PROTOCOL, "Relay cell length field too long. Closing circuit.");
-    return -1;
+    return - END_CIRC_REASON_TORPROTOCOL;
   }
 
   /* either conn is NULL, in which case we've got a control cell, or else
@@ -904,7 +909,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
         connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL,
                             conn->cpath_layer);
         connection_mark_for_close(conn);
-        return -1;
+        return -END_CIRC_REASON_TORPROTOCOL;
       }
       debug(domain,"circ deliver_window now %d.", layer_hint ?
             layer_hint->deliver_window : circ->deliver_window);
@@ -919,7 +924,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
       if (--conn->deliver_window < 0) { /* is it below 0 after decrement? */
         log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
                "(relay data) conn deliver_window below 0. Killing.");
-        return -1; /* somebody's breaking protocol. kill the whole circuit. */
+        return -END_CIRC_REASON_TORPROTOCOL;
       }
 
       stats_n_data_bytes_received += rh.length;
@@ -974,14 +979,14 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
         return 0;
       }
       debug(domain,"Got an extended cell! Yay.");
-      if (circuit_finish_handshake(circ, CELL_CREATED,
-                                   cell->payload+RELAY_HEADER_SIZE) < 0) {
+      if ((reason = circuit_finish_handshake(circ, CELL_CREATED,
+                                       cell->payload+RELAY_HEADER_SIZE)) < 0) {
         warn(domain,"circuit_finish_handshake failed.");
-        return -1;
+        return reason;
       }
-      if (circuit_send_next_onion_skin(circ)<0) {
+      if ((reason=circuit_send_next_onion_skin(circ))<0) {
         info(domain,"circuit_send_next_onion_skin() failed.");
-        return -1;
+        return reason;
       }
       return 0;
     case RELAY_COMMAND_TRUNCATE:
@@ -990,12 +995,17 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
         return 0;
       }
       if (circ->n_conn) {
-        connection_send_destroy(circ->n_circ_id, circ->n_conn);
+        uint8_t reason = *(uint8_t*)(cell->payload + RELAY_HEADER_SIZE);
+        connection_or_send_destroy(circ->n_circ_id, circ->n_conn, reason);
         circuit_set_circid_orconn(circ, 0, NULL, N_CONN_CHANGED);
       }
       debug(LD_EXIT, "Processed 'truncate', replying.");
-      connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED,
-                                   NULL, 0, NULL);
+      {
+        char payload[1];
+        payload[0] = (char)END_CIRC_REASON_REQUESTED;
+        connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED,
+                                     payload, sizeof(payload), NULL);
+      }
       return 0;
     case RELAY_COMMAND_TRUNCATED:
       if (!layer_hint) {
@@ -1008,7 +1018,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
       if (conn) {
         log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
                "'connected' unsupported while open. Closing circ.");
-        return -1;
+        return -END_CIRC_REASON_TORPROTOCOL;
       }
       info(domain,"'connected' received, no conn attached anymore. Ignoring.");
       return 0;
@@ -1055,7 +1065,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
     case RELAY_COMMAND_RESOLVED:
       if (conn) {
         warn(domain,"'resolved' unsupported while open. Closing circ.");
-        return -1;
+        return -END_CIRC_REASON_TORPROTOCOL;
       }
       info(domain,"'resolved' received, no conn attached anymore. Ignoring.");
       return 0;

+ 9 - 9
src/or/rendclient.c

@@ -35,7 +35,7 @@ rend_client_send_establish_rendezvous(circuit_t *circ)
 
   if (crypto_rand(circ->rend_cookie, REND_COOKIE_LEN) < 0) {
     warn(LD_BUG, "Internal error: Couldn't produce random cookie.");
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
     return -1;
   }
   if (connection_edge_send_command(NULL,circ,
@@ -154,8 +154,8 @@ rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc)
 
   return 0;
 err:
-  circuit_mark_for_close(introcirc);
-  circuit_mark_for_close(rendcirc);
+  circuit_mark_for_close(introcirc, END_CIRC_AT_ORIGIN);
+  circuit_mark_for_close(rendcirc, END_CIRC_AT_ORIGIN);
   return -1;
 }
 
@@ -186,7 +186,7 @@ rend_client_introduction_acked(circuit_t *circ,
   if (circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
     warn(LD_PROTOCOL, "Received REND_INTRODUCE_ACK on unexpected circuit %d.",
          circ->n_circ_id);
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
     return -1;
   }
 
@@ -208,7 +208,7 @@ rend_client_introduction_acked(circuit_t *circ,
     }
     /* close the circuit: we won't need it anymore. */
     circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACKED;
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
   } else {
     /* It's a NAK; the introduction point didn't relay our request. */
     circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING;
@@ -226,7 +226,7 @@ rend_client_introduction_acked(circuit_t *circ,
       if (!extend_info) {
         warn(LD_REND, "No introduction points left for %s. Closing.",
              safe_str(circ->rend_query));
-        circuit_mark_for_close(circ);
+        circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
         return -1;
       }
       info(LD_REND,
@@ -340,7 +340,7 @@ rend_client_rendezvous_acked(circuit_t *circ, const char *request,
   if (circ->purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND) {
     warn(LD_PROTOCOL,"Got a rendezvous ack when we weren't expecting one. "
          "Closing circ.");
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
     return -1;
   }
   info(LD_REND,"Got rendezvous ack. This circuit is now ready for "
@@ -362,7 +362,7 @@ rend_client_receive_rendezvous(circuit_t *circ, const char *request,
       || !circ->build_state->pending_final_cpath) {
     warn(LD_PROTOCOL,"Got rendezvous2 cell from hidden service, but not "
          "expecting it. Closing.");
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
     return -1;
   }
 
@@ -408,7 +408,7 @@ rend_client_receive_rendezvous(circuit_t *circ, const char *request,
   circ->build_state->pending_final_cpath = NULL; /* prevent double-free */
   return 0;
  err:
-  circuit_mark_for_close(circ);
+  circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
   return -1;
 }
 

+ 19 - 7
src/or/rendmid.c

@@ -25,6 +25,7 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
   size_t asn1len;
   circuit_t *c;
   char serviceid[REND_SERVICE_ID_LEN+1];
+  int reason = END_CIRC_REASON_INTERNAL;
 
   info(LD_REND,
        "Received an ESTABLISH_INTRO request on circuit %d", circ->p_circ_id);
@@ -32,6 +33,7 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
   if (circ->purpose != CIRCUIT_PURPOSE_OR || circ->n_conn) {
     warn(LD_PROTOCOL,
          "Rejecting ESTABLISH_INTRO on non-OR or non-edge circuit.");
+    reason = END_CIRC_REASON_TORPROTOCOL;
     goto err;
   }
   if (request_len < 2+DIGEST_LEN)
@@ -44,6 +46,7 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
     goto truncated;
   pk = crypto_pk_asn1_decode(request+2, asn1len);
   if (!pk) {
+    reason = END_CIRC_REASON_TORPROTOCOL;
     warn(LD_PROTOCOL, "Couldn't decode public key.");
     goto err;
   }
@@ -57,6 +60,7 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
   }
   if (memcmp(expected_digest, request+2+asn1len, DIGEST_LEN)) {
     warn(LD_PROTOCOL, "Hash of session info was not as expected.");
+    reason = END_CIRC_REASON_TORPROTOCOL;
     goto err;
   }
   /* Rest of body: signature of previous data */
@@ -65,6 +69,7 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
                                        request_len-(2+DIGEST_LEN+asn1len))<0) {
     warn(LD_PROTOCOL,
          "Incorrect signature on ESTABLISH_INTRO cell; rejecting.");
+    reason = END_CIRC_REASON_TORPROTOCOL;
     goto err;
   }
 
@@ -85,7 +90,7 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
                                 c,pk_digest,CIRCUIT_PURPOSE_INTRO_POINT))) {
     info(LD_REND, "Replacing old circuit %d for service %s",
          c->p_circ_id, safe_str(serviceid));
-    circuit_mark_for_close(c);
+    circuit_mark_for_close(c, END_CIRC_REASON_REQUESTED);
   }
 
   /* Acknowledge the request. */
@@ -107,9 +112,10 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
   return 0;
  truncated:
   warn(LD_PROTOCOL, "Rejecting truncated ESTABLISH_INTRO cell.");
+  reason = END_CIRC_REASON_TORPROTOCOL;
  err:
   if (pk) crypto_free_pk_env(pk);
-  circuit_mark_for_close(circ);
+  circuit_mark_for_close(circ, reason);
   return -1;
 }
 
@@ -168,7 +174,7 @@ rend_mid_introduce(circuit_t *circ, const char *request, size_t request_len)
   if (connection_edge_send_command(NULL,circ,RELAY_COMMAND_INTRODUCE_ACK,
                                    NULL,0,NULL)) {
     warn(LD_GENERAL, "Unable to send INTRODUCE_ACK cell to Tor client.");
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
     return -1;
   }
 
@@ -179,7 +185,8 @@ rend_mid_introduce(circuit_t *circ, const char *request, size_t request_len)
   if (connection_edge_send_command(NULL,circ,RELAY_COMMAND_INTRODUCE_ACK,
                                    nak_body, 1, NULL)) {
     warn(LD_GENERAL, "Unable to send NAK to Tor client.");
-    circuit_mark_for_close(circ); /* Is this right? */
+    /* Is this right? */
+    circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
   }
   return -1;
 }
@@ -192,6 +199,7 @@ rend_mid_establish_rendezvous(circuit_t *circ, const char *request,
                               size_t request_len)
 {
   char hexid[9];
+  int reason = END_CIRC_REASON_TORPROTOCOL;
 
   if (circ->purpose != CIRCUIT_PURPOSE_OR || circ->n_conn) {
     warn(LD_PROTOCOL,
@@ -214,6 +222,7 @@ rend_mid_establish_rendezvous(circuit_t *circ, const char *request,
                                    RELAY_COMMAND_RENDEZVOUS_ESTABLISHED,
                                    "", 0, NULL)<0) {
     warn(LD_PROTOCOL, "Couldn't send RENDEZVOUS_ESTABLISHED cell.");
+    reason = END_CIRC_REASON_INTERNAL;
     goto err;
   }
 
@@ -227,7 +236,7 @@ rend_mid_establish_rendezvous(circuit_t *circ, const char *request,
 
   return 0;
  err:
-  circuit_mark_for_close(circ);
+  circuit_mark_for_close(circ, reason);
   return -1;
 }
 
@@ -240,7 +249,7 @@ rend_mid_rendezvous(circuit_t *circ, const char *request, size_t request_len)
 {
   circuit_t *rend_circ;
   char hexid[9];
-
+  int reason = END_CIRC_REASON_INTERNAL;
   base16_encode(hexid,9,request,request_len<4?request_len:4);
 
   if (request_len>=4) {
@@ -252,6 +261,7 @@ rend_mid_rendezvous(circuit_t *circ, const char *request, size_t request_len)
     info(LD_REND,
          "Tried to complete rendezvous on non-OR or non-edge circuit %d.",
          circ->p_circ_id);
+    reason = END_CIRC_REASON_TORPROTOCOL;
     goto err;
   }
 
@@ -259,6 +269,7 @@ rend_mid_rendezvous(circuit_t *circ, const char *request, size_t request_len)
     warn(LD_PROTOCOL,
          "Rejecting RENDEZVOUS1 cell with bad length (%d) on circuit %d.",
          (int)request_len, circ->p_circ_id);
+    reason = END_CIRC_REASON_TORPROTOCOL;
     goto err;
   }
 
@@ -267,6 +278,7 @@ rend_mid_rendezvous(circuit_t *circ, const char *request, size_t request_len)
     warn(LD_PROTOCOL,
          "Rejecting RENDEZVOUS1 cell with unrecognized rendezvous cookie %s.",
          hexid);
+    reason = END_CIRC_REASON_TORPROTOCOL;
     goto err;
   }
 
@@ -295,7 +307,7 @@ rend_mid_rendezvous(circuit_t *circ, const char *request, size_t request_len)
 
   return 0;
  err:
-  circuit_mark_for_close(circ);
+  circuit_mark_for_close(circ, reason);
   return -1;
 }
 

+ 4 - 4
src/or/rendservice.c

@@ -605,7 +605,7 @@ rend_service_introduce(circuit_t *circuit, const char *request,
   return 0;
  err:
   if (dh) crypto_dh_free(dh);
-  if (launched) circuit_mark_for_close(launched);
+  if (launched) circuit_mark_for_close(launched, END_CIRC_AT_ORIGIN);
   if (extend_info) extend_info_free(extend_info);
   return -1;
 }
@@ -751,7 +751,7 @@ rend_service_intro_has_opened(circuit_t *circuit)
 
   return;
  err:
-  circuit_mark_for_close(circuit);
+  circuit_mark_for_close(circuit, END_CIRC_AT_ORIGIN);
 }
 
 /** Called when we get an INTRO_ESTABLISHED cell; mark the circuit as a
@@ -778,7 +778,7 @@ rend_service_intro_established(circuit_t *circuit, const char *request,
 
   return 0;
  err:
-  circuit_mark_for_close(circuit);
+  circuit_mark_for_close(circuit, END_CIRC_AT_ORIGIN);
   return -1;
 }
 
@@ -852,7 +852,7 @@ rend_service_rendezvous_has_opened(circuit_t *circuit)
 
   return;
  err:
-  circuit_mark_for_close(circuit);
+  circuit_mark_for_close(circuit, END_CIRC_AT_ORIGIN);
 }
 
 /*