Explorar o código

Add reasons to DESTROY and RELAY_TRUNCATED cells.

svn:r5734
Nick Mathewson %!s(int64=20) %!d(string=hai) anos
pai
achega
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.
       other nodes *are* reachable.
     o Make EntryNodes and StrictEntrynodes do what we want.
     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.
 N . Only use a routerdesc if you recognize its hash.
     o (Must defer till dirservers are upgraded to latest code, which
     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.
       - Test.
     - Non-directories don't need to keep descriptors in memory.
     - 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.
 R - Christian Grothoff's attack of infinite-length circuit.
     the solution is to have a separate 'extend-data' cell type
     the solution is to have a separate 'extend-data' cell type
     which is used for the first N data cells, and only
     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
     those, if circuits aren't working and it's a pattern we recognize
     ("port 443 worked once and port 9001 keeps not working").
     ("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
 N . Additional controller features
       o Find a way to make event info more extensible
       o Find a way to make event info more extensible
       - change circuit status events to give more details, like purpose,
       - 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.
       CREATE:  Payload contains the handshake challenge.
       CREATED: Payload contains the handshake response.
       CREATED: Payload contains the handshake response.
       RELAY:   Payload contains the relay header and relay body.
       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
    Upon receiving any other value for the command field, an OR must
    drop the cell.
    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
    RELAY_TRUNCATED cell towards the OP; the node farther from the OP
    should send a DESTROY cell down the circuit.
    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
 4.5. Routing relay cells
 
 
    When an OR receives a RELAY cell, it checks the cell's circID and
    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 ||
   if (onion_pick_cpath_exit(circ, info) < 0 ||
       onion_populate_cpath(circ) < 0) {
       onion_populate_cpath(circ) < 0) {
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
     return NULL;
     return NULL;
   }
   }
 
 
   control_event_circuit_status(circ, CIRC_EVENT_LAUNCHED);
   control_event_circuit_status(circ, CIRC_EVENT_LAUNCHED);
 
 
   if (circuit_handle_first_hop(circ) < 0) {
   if (circuit_handle_first_hop(circ) < 0) {
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
     return NULL;
     return NULL;
   }
   }
   return circ;
   return circ;
@@ -420,7 +420,7 @@ circuit_n_conn_done(connection_t *or_conn, int status)
                 DIGEST_LEN)) {
                 DIGEST_LEN)) {
       if (!status) { /* or_conn failed; close circ */
       if (!status) { /* or_conn failed; close circ */
         info(LD_CIRC,"or_conn failed. Closing 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;
         continue;
       }
       }
       debug(LD_CIRC,"Found circ %d, sending create cell.", circ->n_circ_id);
       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) {
         if (circuit_send_next_onion_skin(circ) < 0) {
           info(LD_CIRC,
           info(LD_CIRC,
                "send_next_onion_skin failed; circuit marked for closing.");
                "send_next_onion_skin failed; circuit marked for closing.");
-          circuit_mark_for_close(circ);
+          circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
           continue;
           continue;
           /* XXX could this be bad, eg if next_onion_skin failed because conn
           /* XXX could this be bad, eg if next_onion_skin failed because conn
            *     died? */
            *     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 */
         /* pull the create cell out of circ->onionskin, and send it */
         tor_assert(circ->onionskin);
         tor_assert(circ->onionskin);
         if (circuit_deliver_create_cell(circ,CELL_CREATE,circ->onionskin)<0) {
         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;
           continue;
         }
         }
         tor_free(circ->onionskin);
         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
  * Otherwise, we need to build a relay extend cell and send it
  * forward.
  * 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
 int
 circuit_send_next_onion_skin(circuit_t *circ)
 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),
                             &(circ->cpath->dh_handshake_state),
                             payload) < 0) {
                             payload) < 0) {
         warn(LD_CIRC,"onion_skin_create (first hop) failed.");
         warn(LD_CIRC,"onion_skin_create (first hop) failed.");
-        return -1;
+        return - END_CIRC_REASON_INTERNAL;
       }
       }
     } else {
     } else {
       /* We are not an OR, and we're building the first hop of a circuit to a
       /* 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)
     if (circuit_deliver_create_cell(circ, cell_type, payload) < 0)
-      return -1;
+      return - END_CIRC_REASON_RESOURCELIMIT;
 
 
     circ->cpath->state = CPATH_STATE_AWAITING_KEYS;
     circ->cpath->state = CPATH_STATE_AWAITING_KEYS;
     circuit_set_state(circ, CIRCUIT_STATE_BUILDING);
     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,
     if (onion_skin_create(hop->extend_info->onion_key,
                           &(hop->dh_handshake_state), onionskin) < 0) {
                           &(hop->dh_handshake_state), onionskin) < 0) {
       warn(LD_CIRC,"onion_skin_create failed.");
       warn(LD_CIRC,"onion_skin_create failed.");
-      return -1;
+      return - END_CIRC_REASON_INTERNAL;
     }
     }
 
 
     debug(LD_CIRC,"Sending extend relay cell.");
     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);
       n_conn = connection_or_connect(circ->n_addr, circ->n_port, id_digest);
       if (!n_conn) {
       if (!n_conn) {
         info(LD_CIRC,"Launching n_conn failed. Closing circuit.");
         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;
         return 0;
       }
       }
       debug(LD_CIRC,"connecting in progress (or finished). Good.");
       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
  * Calculate the appropriate keys and digests, make sure KH is
  * correct, and initialize this hop of the cpath.
  * 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
 int
 circuit_finish_handshake(circuit_t *circ, uint8_t reply_type, char *reply)
 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);
     hop = onion_next_hop_in_cpath(circ->cpath);
     if (!hop) { /* got an extended when we're all done? */
     if (!hop) { /* got an extended when we're all done? */
       warn(LD_PROTOCOL,"got extended when circ already built? Closing.");
       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);
   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,
     if (onion_skin_client_handshake(hop->dh_handshake_state, reply, keys,
                                     DIGEST_LEN*2+CIPHER_KEY_LEN*2) < 0) {
                                     DIGEST_LEN*2+CIPHER_KEY_LEN*2) < 0) {
       warn(LD_CIRC,"onion_skin_client_handshake failed.");
       warn(LD_CIRC,"onion_skin_client_handshake failed.");
-      return -1;
+      return -END_CIRC_REASON_TORPROTOCOL;
     }
     }
     /* Remember hash of g^xy */
     /* Remember hash of g^xy */
     memcpy(hop->handshake_digest, reply+DH_KEY_LEN, DIGEST_LEN);
     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,
     if (fast_client_handshake(hop->fast_handshake_state, reply, keys,
                               DIGEST_LEN*2+CIPHER_KEY_LEN*2) < 0) {
                               DIGEST_LEN*2+CIPHER_KEY_LEN*2) < 0) {
       warn(LD_CIRC,"fast_client_handshake failed.");
       warn(LD_CIRC,"fast_client_handshake failed.");
-      return -1;
+      return -END_CIRC_REASON_TORPROTOCOL;
     }
     }
     memcpy(hop->handshake_digest, reply+DIGEST_LEN, DIGEST_LEN);
     memcpy(hop->handshake_digest, reply+DIGEST_LEN, DIGEST_LEN);
   } else {
   } else {
     warn(LD_PROTOCOL,"CREATED cell type did not match CREATE cell type.");
     warn(LD_PROTOCOL,"CREATED cell type did not match CREATE cell type.");
-    return -1;
+    return -END_CIRC_REASON_TORPROTOCOL;
   }
   }
 
 
   if (hop->dh_handshake_state) {
   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));
   memset(hop->fast_handshake_state, 0, sizeof(hop->fast_handshake_state));
 
 
   if (circuit_init_cpath_crypto(hop, keys, 0)<0) {
   if (circuit_init_cpath_crypto(hop, keys, 0)<0) {
-    return -1;
+    return -END_CIRC_REASON_TORPROTOCOL;
   }
   }
 
 
   hop->state = CPATH_STATE_OPEN;
   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,
    *     means that a connection broke or an extend failed. For now,
    *     just give up.
    *     just give up.
    */
    */
-  circuit_mark_for_close(circ);
+  circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
   return 0;
   return 0;
 
 
 #if 0
 #if 0
@@ -1375,12 +1375,13 @@ circuit_append_new_exit(circuit_t *circ, extend_info_t *info)
 int
 int
 circuit_extend_to_new_exit(circuit_t *circ, extend_info_t *info)
 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_append_new_exit(circ, info);
   circuit_set_state(circ, CIRCUIT_STATE_BUILDING);
   circuit_set_state(circ, CIRCUIT_STATE_BUILDING);
   if (circuit_send_next_onion_skin(circ)<0) {
   if (circuit_send_next_onion_skin(circ)<0) {
     warn(LD_CIRC, "Couldn't extend circuit to new point '%s'.",
     warn(LD_CIRC, "Couldn't extend circuit to new point '%s'.",
          info->nickname);
          info->nickname);
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
     return -1;
     return -1;
   }
   }
   return 0;
   return 0;
@@ -1731,7 +1732,7 @@ static INLINE int
 is_an_entry_node(char *digest)
 is_an_entry_node(char *digest)
 {
 {
   SMARTLIST_FOREACH(entry_nodes, entry_node_t *, entry,
   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 1;
                    );
                    );
   return 0;
   return 0;

+ 24 - 7
src/or/circuitlist.c

@@ -470,7 +470,7 @@ circuit_get_by_edge_conn(connection_t *conn)
  * been marked already.
  * been marked already.
  */
  */
 void
 void
-circuit_unlink_all_from_or_conn(connection_t *conn)
+circuit_unlink_all_from_or_conn(connection_t *conn, int reason)
 {
 {
   circuit_t *circ;
   circuit_t *circ;
   for (circ = global_circuitlist; circ; circ = circ->next) {
   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)
       if (circ->p_conn == conn)
         circuit_set_circid_orconn(circ, 0, NULL, P_CONN_CHANGED);
         circuit_set_circid_orconn(circ, 0, NULL, P_CONN_CHANGED);
       if (!circ->marked_for_close)
       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) &&
     if (CIRCUIT_IS_ORIGIN(circ) &&
         !circ->marked_for_close &&
         !circ->marked_for_close &&
         !circ->timestamp_dirty)
         !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.
  *     rendezvous stream), then mark the other circuit to close as well.
  */
  */
 void
 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;
   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);
         circ->marked_for_close_file, circ->marked_for_close);
     return;
     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) {
   if (circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) {
     onion_pending_remove(circ);
     onion_pending_remove(circ);
@@ -698,7 +715,7 @@ _circuit_mark_for_close(circuit_t *circ, int line, const char *file)
   }
   }
 
 
   if (circ->n_conn)
   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)
   for (conn=circ->n_streams; conn; conn=conn->next_stream)
     connection_edge_destroy(circ->n_circ_id, conn);
     connection_edge_destroy(circ->n_circ_id, conn);
   while (circ->resolving_streams) {
   while (circ->resolving_streams) {
@@ -714,7 +731,7 @@ _circuit_mark_for_close(circuit_t *circ, int line, const char *file)
     conn->on_circuit = NULL;
     conn->on_circuit = NULL;
   }
   }
   if (circ->p_conn)
   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)
   for (conn=circ->p_streams; conn; conn=conn->next_stream)
     connection_edge_destroy(circ->p_circ_id, conn);
     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) {
     if (!circ->rend_splice->marked_for_close) {
     if (!circ->rend_splice->marked_for_close) {
       /* do this after marking this circuit, to avoid infinite recursion. */
       /* 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;
     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_state_to_string(victim->state), victim->purpose);
 
 
     circuit_log_path(LOG_INFO,LD_CIRC,victim);
     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. */
       /* Inform any pending (not attached) circs that they should give up. */
       circuit_n_conn_done(conn, 0);
       circuit_n_conn_done(conn, 0);
       /* Now close all the attached circuits on it. */
       /* 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;
       return;
     }
     }
     case CONN_TYPE_AP:
     case CONN_TYPE_AP:
@@ -568,7 +568,7 @@ circuit_expire_old_circuits(void)
       /* (only general and purpose_c circs can get dirty) */
       /* (only general and purpose_c circs can get dirty) */
       tor_assert(!circ->n_streams);
       tor_assert(!circ->n_streams);
       tor_assert(circ->purpose <= CIRCUIT_PURPOSE_C_REND_JOINED);
       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) &&
     } else if (!circ->timestamp_dirty && CIRCUIT_IS_ORIGIN(circ) &&
                circ->state == CIRCUIT_STATE_OPEN &&
                circ->state == CIRCUIT_STATE_OPEN &&
                circ->purpose == CIRCUIT_PURPOSE_C_GENERAL) {
                circ->purpose == CIRCUIT_PURPOSE_C_GENERAL) {
@@ -576,7 +576,7 @@ circuit_expire_old_circuits(void)
       if (circ->timestamp_created + CIRCUIT_UNUSED_CIRC_TIMEOUT < now) {
       if (circ->timestamp_created + CIRCUIT_UNUSED_CIRC_TIMEOUT < now) {
         debug(LD_CIRC,"Closing circuit that has been unused for %d seconds.",
         debug(LD_CIRC,"Closing circuit that has been unused for %d seconds.",
               (int)(now - circ->timestamp_created));
               (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
   /* For now, we only use testing circuits to see if our ORPort is
      reachable. But we remember reachability in onionskin_answer(),
      reachable. But we remember reachability in onionskin_answer(),
      so there's no need to record anything here. Just close the circ. */
      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. */
 /** 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()) {
   if (we_are_hibernating()) {
     info(LD_OR,"Received create cell but we're shutting down. Sending back "
     info(LD_OR,"Received create cell but we're shutting down. Sending back "
          "destroy.");
          "destroy.");
-    connection_send_destroy(cell->circ_id, conn);
+    connection_or_send_destroy(cell->circ_id, conn,
+                               END_CIRC_REASON_HIBERNATING);
     return;
     return;
   }
   }
 
 
@@ -214,7 +215,7 @@ command_process_create_cell(cell_t *cell, connection_t *conn)
     /* hand it off to the cpuworkers, and then return */
     /* hand it off to the cpuworkers, and then return */
     if (assign_to_cpuworker(NULL, CPUWORKER_TASK_ONION, circ) < 0) {
     if (assign_to_cpuworker(NULL, CPUWORKER_TASK_ONION, circ) < 0) {
       warn(LD_GENERAL,"Failed to hand off onionskin. Closing.");
       warn(LD_GENERAL,"Failed to hand off onionskin. Closing.");
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
       return;
       return;
     }
     }
     debug(LD_OR,"success: handed off onionskin.");
     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);
     tor_assert(cell->command == CELL_CREATE_FAST);
     if (fast_server_handshake(cell->payload, reply, keys, sizeof(keys))<0) {
     if (fast_server_handshake(cell->payload, reply, keys, sizeof(keys))<0) {
       warn(LD_OR,"Failed to generate key material. Closing.");
       warn(LD_OR,"Failed to generate key material. Closing.");
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
       return;
       return;
     }
     }
     if (onionskin_answer(circ, CELL_CREATED_FAST, reply, keys)<0) {
     if (onionskin_answer(circ, CELL_CREATED_FAST, reply, keys)<0) {
       warn(LD_OR,"Failed to reply to CREATE_FAST cell. Closing.");
       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;
       return;
     }
     }
   }
   }
@@ -261,7 +262,7 @@ command_process_created_cell(cell_t *cell, connection_t *conn)
   if (circ->n_circ_id != cell->circ_id) {
   if (circ->n_circ_id != cell->circ_id) {
     log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,
     log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,
            "got created cell from OPward? Closing.");
            "got created cell from OPward? Closing.");
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL);
     return;
     return;
   }
   }
 
 
@@ -269,13 +270,14 @@ command_process_created_cell(cell_t *cell, connection_t *conn)
     debug(LD_OR,"at OP. Finishing handshake.");
     debug(LD_OR,"at OP. Finishing handshake.");
     if (circuit_finish_handshake(circ, cell->command, cell->payload) < 0) {
     if (circuit_finish_handshake(circ, cell->command, cell->payload) < 0) {
       warn(LD_OR,"circuit_finish_handshake failed.");
       warn(LD_OR,"circuit_finish_handshake failed.");
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
       return;
       return;
     }
     }
     debug(LD_OR,"Moving to next skin.");
     debug(LD_OR,"Moving to next skin.");
     if (circuit_send_next_onion_skin(circ) < 0) {
     if (circuit_send_next_onion_skin(circ) < 0) {
       info(LD_OR,"circuit_send_next_onion_skin failed.");
       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;
       return;
     }
     }
   } else { /* pack it into an extended relay cell, and send it. */
   } 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)
 command_process_relay_cell(cell_t *cell, connection_t *conn)
 {
 {
   circuit_t *circ;
   circuit_t *circ;
+  int reason;
 
 
   circ = circuit_get_by_circid_orconn(cell->circ_id, conn);
   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) {
   if (circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) {
     log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,"circuit in create_wait. Closing.");
     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;
     return;
   }
   }
 
 
   if (cell->circ_id == circ->p_circ_id) { /* it's an outgoing cell */
   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 "
       log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,"circuit_receive_relay_cell "
              "(forward) failed. Closing.");
              "(forward) failed. Closing.");
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, -reason);
       return;
       return;
     }
     }
   } else { /* it's an ingoing cell */
   } 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 "
       log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,"circuit_receive_relay_cell "
              "(backward) failed. Closing.");
              "(backward) failed. Closing.");
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, -reason);
       return;
       return;
     }
     }
   }
   }
@@ -342,9 +347,10 @@ static void
 command_process_destroy_cell(cell_t *cell, connection_t *conn)
 command_process_destroy_cell(cell_t *cell, connection_t *conn)
 {
 {
   circuit_t *circ;
   circuit_t *circ;
+  uint8_t reason;
 
 
   circ = circuit_get_by_circid_orconn(cell->circ_id, conn);
   circ = circuit_get_by_circid_orconn(cell->circ_id, conn);
-
+  reason = (uint8_t)cell->payload[0];
   if (!circ) {
   if (!circ) {
     info(LD_OR,"unknown circuit %d on connection from %s:%d. Dropping.",
     info(LD_OR,"unknown circuit %d on connection from %s:%d. Dropping.",
          cell->circ_id, conn->address, conn->port);
          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) {
   if (cell->circ_id == circ->p_circ_id) {
     /* the destroy came from behind */
     /* the destroy came from behind */
     circuit_set_circid_orconn(circ, 0, NULL, P_CONN_CHANGED);
     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 */
   } else { /* the destroy came from ahead */
     circuit_set_circid_orconn(circ, 0, NULL, N_CONN_CHANGED);
     circuit_set_circid_orconn(circ, 0, NULL, N_CONN_CHANGED);
     if (CIRCUIT_IS_ORIGIN(circ)) {
     if (CIRCUIT_IS_ORIGIN(circ)) {
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, reason);
     } else {
     } else {
+      char payload[1];
       debug(LD_OR, "Delivering 'truncated' back.");
       debug(LD_OR, "Delivering 'truncated' back.");
+      payload[0] = (char)reason;
       connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED,
       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
 static void
 connection_consider_empty_buckets(connection_t *conn)
 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
       /* if it failed, it means we have our package/delivery windows set
          wrong compared to our max outbuf size. close the whole circuit. */
          wrong compared to our max outbuf size. close the whole circuit. */
       warn(LD_NET,"write_to_buf failed. Closing circuit (fd %d).", conn->s);
       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 {
     } else {
       warn(LD_NET,"write_to_buf failed. Closing connection (fd %d).", conn->s);
       warn(LD_NET,"write_to_buf failed. Closing connection (fd %d).", conn->s);
       connection_mark_for_close(conn);
       connection_mark_for_close(conn);
@@ -1784,28 +1786,6 @@ connection_state_is_connecting(connection_t *conn)
   return 0;
   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
 /** Alloocates a base64'ed authenticator for use in http or https
  * auth, based on the input string <b>authenticator</b>. Returns it
  * auth, based on the input string <b>authenticator</b>. Returns it
  * if success, else returns NULL. */
  * if success, else returns NULL. */
@@ -1829,9 +1809,12 @@ alloc_http_authenticator(const char *authenticator)
   return base64_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
 static void
 client_check_address_changed(int sock)
 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);
   ap_conn->stream_id = get_unique_stream_id_by_circ(circ);
   if (ap_conn->stream_id==0) {
   if (ap_conn->stream_id==0) {
     connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL);
     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;
     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);
   ap_conn->stream_id = get_unique_stream_id_by_circ(circ);
   if (ap_conn->stream_id==0) {
   if (ap_conn->stream_id==0) {
     connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL);
     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;
     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,
       connection_edge_end(n_stream, END_STREAM_REASON_EXITPOLICY,
                           n_stream->cpath_layer);
                           n_stream->cpath_layer);
       connection_free(n_stream);
       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);
       tor_free(address);
       return 0;
       return 0;
     }
     }

+ 23 - 0
src/or/connection_or.c

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

+ 2 - 2
src/or/cpuworker.c

@@ -169,7 +169,7 @@ connection_cpu_process_inbuf(connection_t *conn)
       debug(LD_OR,
       debug(LD_OR,
             "decoding onionskin failed. (Old key or bad software.) Closing.");
             "decoding onionskin failed. (Old key or bad software.) Closing.");
       if (circ)
       if (circ)
-        circuit_mark_for_close(circ);
+        circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL);
       goto done_processing;
       goto done_processing;
     }
     }
     if (!circ) {
     if (!circ) {
@@ -185,7 +185,7 @@ connection_cpu_process_inbuf(connection_t *conn)
     if (onionskin_answer(circ, CELL_CREATED, buf+TAG_LEN,
     if (onionskin_answer(circ, CELL_CREATED, buf+TAG_LEN,
                          buf+TAG_LEN+ONIONSKIN_REPLY_LEN) < 0) {
                          buf+TAG_LEN+ONIONSKIN_REPLY_LEN) < 0) {
       warn(LD_OR,"onionskin_answer failed. Closing.");
       warn(LD_OR,"onionskin_answer failed. Closing.");
-      circuit_mark_for_close(circ);
+      circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
       goto done_processing;
       goto done_processing;
     }
     }
     debug(LD_OR,"onionskin_answer succeeded. Yay.");
     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);
     onion_pending_remove(ol_list->circ);
     info(LD_CIRC,
     info(LD_CIRC,
          "Circuit create request is too old; cancelling due to overload.");
          "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;
   return 0;
 }
 }

+ 20 - 5
src/or/or.h

@@ -481,6 +481,19 @@ typedef enum {
 #define RESOLVED_TYPE_ERROR_TRANSIENT 0xF0
 #define RESOLVED_TYPE_ERROR_TRANSIENT 0xF0
 #define RESOLVED_TYPE_ERROR 0xF1
 #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. */
 /** Length of 'y' portion of 'y.onion' URL. */
 #define REND_SERVICE_ID_LEN 16
 #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);
 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);
 int circuit_id_used_on_conn(uint16_t circ_id, connection_t *conn);
 circuit_t *circuit_get_by_edge_conn(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_global_id(uint32_t id);
 circuit_t *circuit_get_by_rend_query_and_purpose(const char *rend_query,
 circuit_t *circuit_get_by_rend_query_and_purpose(const char *rend_query,
                                                  uint8_t purpose);
                                                  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);
                                        int need_capacity, int internal);
 void circuit_mark_all_unused_circs(void);
 void circuit_mark_all_unused_circs(void);
 void circuit_expire_all_dirty_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_cpath_layer_ok(const crypt_path_t *cp);
 void assert_circuit_ok(const circuit_t *c);
 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_open(connection_t *conn);
 int connection_state_is_connecting(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);
 char *alloc_http_authenticator(const char *authenticator);
 
 
 void assert_connection_ok(connection_t *conn, time_t now);
 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);
 int connection_tls_continue_handshake(connection_t *conn);
 
 
 void connection_or_write_cell_to_buf(const cell_t *cell, 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 ***************************/
 /********************************* 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.
  *    connection_edge.
  *  - Else connection_or_write_cell_to_buf to the conn on the other
  *  - Else connection_or_write_cell_to_buf to the conn on the other
  *    side of the circuit.
  *    side of the circuit.
+ *
+ * Return -reason on failure.
  */
  */
 int
 int
 circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, int cell_direction)
 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;
   connection_t *conn=NULL;
   crypt_path_t *layer_hint=NULL;
   crypt_path_t *layer_hint=NULL;
   char recognized=0;
   char recognized=0;
+  int reason;
 
 
   tor_assert(cell);
   tor_assert(cell);
   tor_assert(circ);
   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) {
     if (cell_direction == CELL_DIRECTION_OUT) {
       ++stats_n_relay_cells_delivered;
       ++stats_n_relay_cells_delivered;
       debug(LD_OR,"Sending away from origin.");
       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,
         log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
                "connection_edge_process_relay_cell (away from origin) "
                "connection_edge_process_relay_cell (away from origin) "
                "failed.");
                "failed.");
-        return -1;
+        return reason;
       }
       }
     }
     }
     if (cell_direction == CELL_DIRECTION_IN) {
     if (cell_direction == CELL_DIRECTION_IN) {
       ++stats_n_relay_cells_delivered;
       ++stats_n_relay_cells_delivered;
       debug(LD_OR,"Sending to origin.");
       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.");
         warn(LD_OR,"connection_edge_process_relay_cell (at origin) failed.");
-        return -1;
+        return reason;
       }
       }
     }
     }
     return 0;
     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 ==
       tor_assert(circ->rend_splice->purpose ==
                  CIRCUIT_PURPOSE_REND_ESTABLISHED);
                  CIRCUIT_PURPOSE_REND_ESTABLISHED);
       cell->circ_id = circ->rend_splice->p_circ_id;
       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 "
         warn(LD_REND, "Error relaying cell across rendezvous; closing "
              "circuits");
              "circuits");
         /* XXXX Do this here, or just return -1? */
         /* 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;
       return 0;
     }
     }
     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
            "Didn't recognize cell, but circ stops here! Closing circ.");
            "Didn't recognize cell, but circ stops here! Closing circ.");
-    return -1;
+    return -END_CIRC_REASON_TORPROTOCOL;
   }
   }
 
 
   debug(LD_OR,"Passing on unrecognized cell.");
   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)
   if (circuit_package_relay_cell(&cell, circ, cell_direction, cpath_layer)
       < 0) {
       < 0) {
     warn(LD_BUG,"circuit_package_relay_cell failed. Closing.");
     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 -1;
   }
   }
   return 0;
   return 0;
@@ -655,7 +659,7 @@ connection_edge_process_end_not_open(
       warn(LD_PROTOCOL,
       warn(LD_PROTOCOL,
            "Got an end because of %s, but we're not an AP. Closing.",
            "Got an end because of %s, but we're not an AP. Closing.",
            connection_edge_end_reason_str(reason));
            connection_edge_end_reason_str(reason));
-      return -1;
+      return - END_CIRC_REASON_TORPROTOCOL;
     }
     }
     info(LD_APP,"Address '%s' refused due to '%s'. Considering retrying.",
     info(LD_APP,"Address '%s' refused due to '%s'. Considering retrying.",
          safe_str(conn->socks_request->address),
          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
  * 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>.
  * 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
 static int
 connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
 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;
   static int num_seen=0;
   relay_header_t rh;
   relay_header_t rh;
   unsigned domain = layer_hint?LD_APP:LD_EXIT;
   unsigned domain = layer_hint?LD_APP:LD_EXIT;
+  int reason;
 
 
   tor_assert(cell);
   tor_assert(cell);
   tor_assert(circ);
   tor_assert(circ);
@@ -869,7 +874,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
 
 
   if (rh.length > RELAY_PAYLOAD_SIZE) {
   if (rh.length > RELAY_PAYLOAD_SIZE) {
     warn(LD_PROTOCOL, "Relay cell length field too long. Closing circuit.");
     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
   /* 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,
         connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL,
                             conn->cpath_layer);
                             conn->cpath_layer);
         connection_mark_for_close(conn);
         connection_mark_for_close(conn);
-        return -1;
+        return -END_CIRC_REASON_TORPROTOCOL;
       }
       }
       debug(domain,"circ deliver_window now %d.", layer_hint ?
       debug(domain,"circ deliver_window now %d.", layer_hint ?
             layer_hint->deliver_window : circ->deliver_window);
             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? */
       if (--conn->deliver_window < 0) { /* is it below 0 after decrement? */
         log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
         log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
                "(relay data) conn deliver_window below 0. Killing.");
                "(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;
       stats_n_data_bytes_received += rh.length;
@@ -974,14 +979,14 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
         return 0;
         return 0;
       }
       }
       debug(domain,"Got an extended cell! Yay.");
       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.");
         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.");
         info(domain,"circuit_send_next_onion_skin() failed.");
-        return -1;
+        return reason;
       }
       }
       return 0;
       return 0;
     case RELAY_COMMAND_TRUNCATE:
     case RELAY_COMMAND_TRUNCATE:
@@ -990,12 +995,17 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
         return 0;
         return 0;
       }
       }
       if (circ->n_conn) {
       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);
         circuit_set_circid_orconn(circ, 0, NULL, N_CONN_CHANGED);
       }
       }
       debug(LD_EXIT, "Processed 'truncate', replying.");
       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;
       return 0;
     case RELAY_COMMAND_TRUNCATED:
     case RELAY_COMMAND_TRUNCATED:
       if (!layer_hint) {
       if (!layer_hint) {
@@ -1008,7 +1018,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
       if (conn) {
       if (conn) {
         log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
         log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
                "'connected' unsupported while open. Closing circ.");
                "'connected' unsupported while open. Closing circ.");
-        return -1;
+        return -END_CIRC_REASON_TORPROTOCOL;
       }
       }
       info(domain,"'connected' received, no conn attached anymore. Ignoring.");
       info(domain,"'connected' received, no conn attached anymore. Ignoring.");
       return 0;
       return 0;
@@ -1055,7 +1065,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
     case RELAY_COMMAND_RESOLVED:
     case RELAY_COMMAND_RESOLVED:
       if (conn) {
       if (conn) {
         warn(domain,"'resolved' unsupported while open. Closing circ.");
         warn(domain,"'resolved' unsupported while open. Closing circ.");
-        return -1;
+        return -END_CIRC_REASON_TORPROTOCOL;
       }
       }
       info(domain,"'resolved' received, no conn attached anymore. Ignoring.");
       info(domain,"'resolved' received, no conn attached anymore. Ignoring.");
       return 0;
       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) {
   if (crypto_rand(circ->rend_cookie, REND_COOKIE_LEN) < 0) {
     warn(LD_BUG, "Internal error: Couldn't produce random cookie.");
     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;
     return -1;
   }
   }
   if (connection_edge_send_command(NULL,circ,
   if (connection_edge_send_command(NULL,circ,
@@ -154,8 +154,8 @@ rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc)
 
 
   return 0;
   return 0;
 err:
 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;
   return -1;
 }
 }
 
 
@@ -186,7 +186,7 @@ rend_client_introduction_acked(circuit_t *circ,
   if (circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
   if (circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
     warn(LD_PROTOCOL, "Received REND_INTRODUCE_ACK on unexpected circuit %d.",
     warn(LD_PROTOCOL, "Received REND_INTRODUCE_ACK on unexpected circuit %d.",
          circ->n_circ_id);
          circ->n_circ_id);
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
     return -1;
     return -1;
   }
   }
 
 
@@ -208,7 +208,7 @@ rend_client_introduction_acked(circuit_t *circ,
     }
     }
     /* close the circuit: we won't need it anymore. */
     /* close the circuit: we won't need it anymore. */
     circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACKED;
     circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACKED;
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
   } else {
   } else {
     /* It's a NAK; the introduction point didn't relay our request. */
     /* It's a NAK; the introduction point didn't relay our request. */
     circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING;
     circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING;
@@ -226,7 +226,7 @@ rend_client_introduction_acked(circuit_t *circ,
       if (!extend_info) {
       if (!extend_info) {
         warn(LD_REND, "No introduction points left for %s. Closing.",
         warn(LD_REND, "No introduction points left for %s. Closing.",
              safe_str(circ->rend_query));
              safe_str(circ->rend_query));
-        circuit_mark_for_close(circ);
+        circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
         return -1;
         return -1;
       }
       }
       info(LD_REND,
       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) {
   if (circ->purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND) {
     warn(LD_PROTOCOL,"Got a rendezvous ack when we weren't expecting one. "
     warn(LD_PROTOCOL,"Got a rendezvous ack when we weren't expecting one. "
          "Closing circ.");
          "Closing circ.");
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
     return -1;
     return -1;
   }
   }
   info(LD_REND,"Got rendezvous ack. This circuit is now ready for "
   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) {
       || !circ->build_state->pending_final_cpath) {
     warn(LD_PROTOCOL,"Got rendezvous2 cell from hidden service, but not "
     warn(LD_PROTOCOL,"Got rendezvous2 cell from hidden service, but not "
          "expecting it. Closing.");
          "expecting it. Closing.");
-    circuit_mark_for_close(circ);
+    circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
     return -1;
     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 */
   circ->build_state->pending_final_cpath = NULL; /* prevent double-free */
   return 0;
   return 0;
  err:
  err:
-  circuit_mark_for_close(circ);
+  circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
   return -1;
   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;
   size_t asn1len;
   circuit_t *c;
   circuit_t *c;
   char serviceid[REND_SERVICE_ID_LEN+1];
   char serviceid[REND_SERVICE_ID_LEN+1];
+  int reason = END_CIRC_REASON_INTERNAL;
 
 
   info(LD_REND,
   info(LD_REND,
        "Received an ESTABLISH_INTRO request on circuit %d", circ->p_circ_id);
        "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) {
   if (circ->purpose != CIRCUIT_PURPOSE_OR || circ->n_conn) {
     warn(LD_PROTOCOL,
     warn(LD_PROTOCOL,
          "Rejecting ESTABLISH_INTRO on non-OR or non-edge circuit.");
          "Rejecting ESTABLISH_INTRO on non-OR or non-edge circuit.");
+    reason = END_CIRC_REASON_TORPROTOCOL;
     goto err;
     goto err;
   }
   }
   if (request_len < 2+DIGEST_LEN)
   if (request_len < 2+DIGEST_LEN)
@@ -44,6 +46,7 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
     goto truncated;
     goto truncated;
   pk = crypto_pk_asn1_decode(request+2, asn1len);
   pk = crypto_pk_asn1_decode(request+2, asn1len);
   if (!pk) {
   if (!pk) {
+    reason = END_CIRC_REASON_TORPROTOCOL;
     warn(LD_PROTOCOL, "Couldn't decode public key.");
     warn(LD_PROTOCOL, "Couldn't decode public key.");
     goto err;
     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)) {
   if (memcmp(expected_digest, request+2+asn1len, DIGEST_LEN)) {
     warn(LD_PROTOCOL, "Hash of session info was not as expected.");
     warn(LD_PROTOCOL, "Hash of session info was not as expected.");
+    reason = END_CIRC_REASON_TORPROTOCOL;
     goto err;
     goto err;
   }
   }
   /* Rest of body: signature of previous data */
   /* 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) {
                                        request_len-(2+DIGEST_LEN+asn1len))<0) {
     warn(LD_PROTOCOL,
     warn(LD_PROTOCOL,
          "Incorrect signature on ESTABLISH_INTRO cell; rejecting.");
          "Incorrect signature on ESTABLISH_INTRO cell; rejecting.");
+    reason = END_CIRC_REASON_TORPROTOCOL;
     goto err;
     goto err;
   }
   }
 
 
@@ -85,7 +90,7 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
                                 c,pk_digest,CIRCUIT_PURPOSE_INTRO_POINT))) {
                                 c,pk_digest,CIRCUIT_PURPOSE_INTRO_POINT))) {
     info(LD_REND, "Replacing old circuit %d for service %s",
     info(LD_REND, "Replacing old circuit %d for service %s",
          c->p_circ_id, safe_str(serviceid));
          c->p_circ_id, safe_str(serviceid));
-    circuit_mark_for_close(c);
+    circuit_mark_for_close(c, END_CIRC_REASON_REQUESTED);
   }
   }
 
 
   /* Acknowledge the request. */
   /* Acknowledge the request. */
@@ -107,9 +112,10 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
   return 0;
   return 0;
  truncated:
  truncated:
   warn(LD_PROTOCOL, "Rejecting truncated ESTABLISH_INTRO cell.");
   warn(LD_PROTOCOL, "Rejecting truncated ESTABLISH_INTRO cell.");
+  reason = END_CIRC_REASON_TORPROTOCOL;
  err:
  err:
   if (pk) crypto_free_pk_env(pk);
   if (pk) crypto_free_pk_env(pk);
-  circuit_mark_for_close(circ);
+  circuit_mark_for_close(circ, reason);
   return -1;
   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,
   if (connection_edge_send_command(NULL,circ,RELAY_COMMAND_INTRODUCE_ACK,
                                    NULL,0,NULL)) {
                                    NULL,0,NULL)) {
     warn(LD_GENERAL, "Unable to send INTRODUCE_ACK cell to Tor client.");
     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;
     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,
   if (connection_edge_send_command(NULL,circ,RELAY_COMMAND_INTRODUCE_ACK,
                                    nak_body, 1, NULL)) {
                                    nak_body, 1, NULL)) {
     warn(LD_GENERAL, "Unable to send NAK to Tor client.");
     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;
   return -1;
 }
 }
@@ -192,6 +199,7 @@ rend_mid_establish_rendezvous(circuit_t *circ, const char *request,
                               size_t request_len)
                               size_t request_len)
 {
 {
   char hexid[9];
   char hexid[9];
+  int reason = END_CIRC_REASON_TORPROTOCOL;
 
 
   if (circ->purpose != CIRCUIT_PURPOSE_OR || circ->n_conn) {
   if (circ->purpose != CIRCUIT_PURPOSE_OR || circ->n_conn) {
     warn(LD_PROTOCOL,
     warn(LD_PROTOCOL,
@@ -214,6 +222,7 @@ rend_mid_establish_rendezvous(circuit_t *circ, const char *request,
                                    RELAY_COMMAND_RENDEZVOUS_ESTABLISHED,
                                    RELAY_COMMAND_RENDEZVOUS_ESTABLISHED,
                                    "", 0, NULL)<0) {
                                    "", 0, NULL)<0) {
     warn(LD_PROTOCOL, "Couldn't send RENDEZVOUS_ESTABLISHED cell.");
     warn(LD_PROTOCOL, "Couldn't send RENDEZVOUS_ESTABLISHED cell.");
+    reason = END_CIRC_REASON_INTERNAL;
     goto err;
     goto err;
   }
   }
 
 
@@ -227,7 +236,7 @@ rend_mid_establish_rendezvous(circuit_t *circ, const char *request,
 
 
   return 0;
   return 0;
  err:
  err:
-  circuit_mark_for_close(circ);
+  circuit_mark_for_close(circ, reason);
   return -1;
   return -1;
 }
 }
 
 
@@ -240,7 +249,7 @@ rend_mid_rendezvous(circuit_t *circ, const char *request, size_t request_len)
 {
 {
   circuit_t *rend_circ;
   circuit_t *rend_circ;
   char hexid[9];
   char hexid[9];
-
+  int reason = END_CIRC_REASON_INTERNAL;
   base16_encode(hexid,9,request,request_len<4?request_len:4);
   base16_encode(hexid,9,request,request_len<4?request_len:4);
 
 
   if (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,
     info(LD_REND,
          "Tried to complete rendezvous on non-OR or non-edge circuit %d.",
          "Tried to complete rendezvous on non-OR or non-edge circuit %d.",
          circ->p_circ_id);
          circ->p_circ_id);
+    reason = END_CIRC_REASON_TORPROTOCOL;
     goto err;
     goto err;
   }
   }
 
 
@@ -259,6 +269,7 @@ rend_mid_rendezvous(circuit_t *circ, const char *request, size_t request_len)
     warn(LD_PROTOCOL,
     warn(LD_PROTOCOL,
          "Rejecting RENDEZVOUS1 cell with bad length (%d) on circuit %d.",
          "Rejecting RENDEZVOUS1 cell with bad length (%d) on circuit %d.",
          (int)request_len, circ->p_circ_id);
          (int)request_len, circ->p_circ_id);
+    reason = END_CIRC_REASON_TORPROTOCOL;
     goto err;
     goto err;
   }
   }
 
 
@@ -267,6 +278,7 @@ rend_mid_rendezvous(circuit_t *circ, const char *request, size_t request_len)
     warn(LD_PROTOCOL,
     warn(LD_PROTOCOL,
          "Rejecting RENDEZVOUS1 cell with unrecognized rendezvous cookie %s.",
          "Rejecting RENDEZVOUS1 cell with unrecognized rendezvous cookie %s.",
          hexid);
          hexid);
+    reason = END_CIRC_REASON_TORPROTOCOL;
     goto err;
     goto err;
   }
   }
 
 
@@ -295,7 +307,7 @@ rend_mid_rendezvous(circuit_t *circ, const char *request, size_t request_len)
 
 
   return 0;
   return 0;
  err:
  err:
-  circuit_mark_for_close(circ);
+  circuit_mark_for_close(circ, reason);
   return -1;
   return -1;
 }
 }
 
 

+ 4 - 4
src/or/rendservice.c

@@ -605,7 +605,7 @@ rend_service_introduce(circuit_t *circuit, const char *request,
   return 0;
   return 0;
  err:
  err:
   if (dh) crypto_dh_free(dh);
   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);
   if (extend_info) extend_info_free(extend_info);
   return -1;
   return -1;
 }
 }
@@ -751,7 +751,7 @@ rend_service_intro_has_opened(circuit_t *circuit)
 
 
   return;
   return;
  err:
  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
 /** 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;
   return 0;
  err:
  err:
-  circuit_mark_for_close(circuit);
+  circuit_mark_for_close(circuit, END_CIRC_AT_ORIGIN);
   return -1;
   return -1;
 }
 }
 
 
@@ -852,7 +852,7 @@ rend_service_rendezvous_has_opened(circuit_t *circuit)
 
 
   return;
   return;
  err:
  err:
-  circuit_mark_for_close(circuit);
+  circuit_mark_for_close(circuit, END_CIRC_AT_ORIGIN);
 }
 }
 
 
 /*
 /*