/** Process a 'relay' or 'relay_early' <b>cell</b> that just arrived from * <b>conn</b>. Make sure it came in with a recognized circ_id. Pass it on to * circuit_receive_relay_cell() for actual processing. */ static void command_process_relay_cell(cell_t *cell, or_connection_t *conn) { circuit_t *circ; int reason, direction; circ = circuit_get_by_circid_orconn(cell->circ_id, conn); if (!circ) { log_debug(LD_OR, "unknown circuit %d on connection from %s:%d. Dropping.", cell->circ_id, conn->_base.address, conn->_base.port); return; } if (circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,"circuit in create_wait. Closing."); circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); return; } if (CIRCUIT_IS_ORIGIN(circ)) { /* if we're a relay and treating connections with recent local * traffic better, then this is one of them. */ conn->client_used = time(NULL); } if (!CIRCUIT_IS_ORIGIN(circ) && cell->circ_id == TO_OR_CIRCUIT(circ)->p_circ_id) direction = CELL_DIRECTION_OUT; else direction = CELL_DIRECTION_IN; /* If we have a relay_early cell, make sure that it's outbound, and we've * gotten no more than MAX_RELAY_EARLY_CELLS_PER_CIRCUIT of them. */ if (cell->command == CELL_RELAY_EARLY) { if (direction == CELL_DIRECTION_IN) { /* XXX Allow an unlimited number of inbound relay_early cells for * now, for hidden service compatibility. See bug 1038. -RD */ } else { or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); if (or_circ->remaining_relay_early_cells == 0) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Received too many RELAY_EARLY cells on circ %d from %s:%d." " Closing circuit.", cell->circ_id, safe_str(conn->_base.address), conn->_base.port); circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); return; } --or_circ->remaining_relay_early_cells; } } if ((reason = circuit_receive_relay_cell(cell, circ, direction)) < 0) { log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,"circuit_receive_relay_cell " "(%s) failed. Closing.", direction==CELL_DIRECTION_OUT?"forward":"backward"); circuit_mark_for_close(circ, -reason); } }
/** Extend the introduction circuit <b>circ</b> to another valid * introduction point for the hidden service it is trying to connect * to, or mark it and launch a new circuit if we can't extend it. * Return 0 on success or possible success. Return -1 and mark the * introduction circuit for close on permanent failure. * * On failure, the caller is responsible for marking the associated * rendezvous circuit for close. */ static int rend_client_reextend_intro_circuit(origin_circuit_t *circ) { extend_info_t *extend_info; int result; extend_info = rend_client_get_random_intro(circ->rend_data); if (!extend_info) { log_warn(LD_REND, "No usable introduction points left for %s. Closing.", safe_str_client(circ->rend_data->onion_address)); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return -1; } if (circ->remaining_relay_early_cells) { log_info(LD_REND, "Re-extending circ %d, this time to %s.", circ->_base.n_circ_id, safe_str_client(extend_info_describe(extend_info))); result = circuit_extend_to_new_exit(circ, extend_info); } else { log_info(LD_REND, "Closing intro circ %d (out of RELAY_EARLY cells).", circ->_base.n_circ_id); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED); /* connection_ap_handshake_attach_circuit will launch a new intro circ. */ result = 0; } extend_info_free(extend_info); return result; }
/** Process a 'destroy' <b>cell</b> that just arrived from * <b>conn</b>. Find the circ that it refers to (if any). * * If the circ is in state * onionskin_pending, then call onion_pending_remove() to remove it * from the pending onion list (note that if it's already being * processed by the cpuworker, it won't be in the list anymore; but * when the cpuworker returns it, the circuit will be gone, and the * cpuworker response will be dropped). * * Then mark the circuit for close (which marks all edges for close, * and passes the destroy cell onward if necessary). */ static void command_process_destroy_cell(cell_t *cell, or_connection_t *conn) { circuit_t *circ; int reason; circ = circuit_get_by_circid_orconn(cell->circ_id, conn); reason = (uint8_t)cell->payload[0]; if (!circ) { log_info(LD_OR,"unknown circuit %d on connection from %s:%d. Dropping.", cell->circ_id, conn->_base.address, conn->_base.port); return; } log_debug(LD_OR,"Received for circID %d.",cell->circ_id); if (!CIRCUIT_IS_ORIGIN(circ) && cell->circ_id == TO_OR_CIRCUIT(circ)->p_circ_id) { /* the destroy came from behind */ circuit_set_p_circid_orconn(TO_OR_CIRCUIT(circ), 0, NULL); circuit_mark_for_close(circ, reason|END_CIRC_REASON_FLAG_REMOTE); } else { /* the destroy came from ahead */ circuit_set_n_circid_orconn(circ, 0, NULL); if (CIRCUIT_IS_ORIGIN(circ)) { circuit_mark_for_close(circ, reason|END_CIRC_REASON_FLAG_REMOTE); } else { char payload[1]; log_debug(LD_OR, "Delivering 'truncated' back."); payload[0] = (char)reason; relay_send_command_from_edge(0, circ, RELAY_COMMAND_TRUNCATED, payload, sizeof(payload), NULL); } } }
/** Send the establish-rendezvous cell along a rendezvous circuit. if * it fails, mark the circ for close and return -1. else return 0. */ static int rend_client_send_establish_rendezvous(origin_circuit_t *circ) { tor_assert(circ->base_.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND); tor_assert(circ->rend_data); log_info(LD_REND, "Sending an ESTABLISH_RENDEZVOUS cell"); if (crypto_rand(circ->rend_data->rend_cookie, REND_COOKIE_LEN) < 0) { log_warn(LD_BUG, "Internal error: Couldn't produce random cookie."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return -1; } /* Set timestamp_dirty, because circuit_expire_building expects it, * and the rend cookie also means we've used the circ. */ circ->base_.timestamp_dirty = time(NULL); if (relay_send_command_from_edge(0, TO_CIRCUIT(circ), RELAY_COMMAND_ESTABLISH_RENDEZVOUS, circ->rend_data->rend_cookie, REND_COOKIE_LEN, circ->cpath->prev)<0) { /* circ is already marked for close */ log_warn(LD_GENERAL, "Couldn't send ESTABLISH_RENDEZVOUS cell"); return -1; } return 0; }
/** Add <b>circ</b> to the end of ol_list and return 0, except * if ol_list is too long, in which case do nothing and return -1. */ int onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin) { onion_queue_t *tmp; time_t now = time(NULL); if (onionskin->handshake_type > MAX_ONION_HANDSHAKE_TYPE) { log_warn(LD_BUG, "Handshake %d out of range! Dropping.", onionskin->handshake_type); return -1; } tmp = tor_malloc_zero(sizeof(onion_queue_t)); tmp->circ = circ; tmp->handshake_type = onionskin->handshake_type; tmp->onionskin = onionskin; tmp->when_added = now; if (!have_room_for_onionskin(onionskin->handshake_type)) { #define WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL (60) static ratelim_t last_warned = RATELIM_INIT(WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL); char *m; if (onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR && (m = rate_limit_log(&last_warned, approx_time()))) { log_warn(LD_GENERAL, "Your computer is too slow to handle this many circuit " "creation requests! Please consider using the " "MaxAdvertisedBandwidth config option or choosing a more " "restricted exit policy.%s",m); tor_free(m); } tor_free(tmp); return -1; } ++ol_entries[onionskin->handshake_type]; log_info(LD_OR, "New create (%s). Queues now ntor=%d and tap=%d.", onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap", ol_entries[ONION_HANDSHAKE_TYPE_NTOR], ol_entries[ONION_HANDSHAKE_TYPE_TAP]); circ->onionqueue_entry = tmp; TOR_TAILQ_INSERT_TAIL(&ol_list[onionskin->handshake_type], tmp, next); /* cull elderly requests. */ while (1) { onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list[onionskin->handshake_type]); if (now - head->when_added < (time_t)ONIONQUEUE_WAIT_CUTOFF) break; circ = head->circ; circ->onionqueue_entry = NULL; onion_queue_entry_remove(head); log_info(LD_CIRC, "Circuit create request is too old; canceling due to overload."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT); } return 0; }
/* Helper function that registers <b>circ</b> with <b>token</b> on the HS circuitmap. This function steals reference of <b>token</b>. */ static void hs_circuitmap_register_impl(circuit_t *circ, hs_token_t *token) { tor_assert(circ); tor_assert(token); tor_assert(the_hs_circuitmap); /* If this circuit already has a token, clear it. */ if (circ->hs_token) { hs_circuitmap_remove_circuit(circ); } /* Kill old circuits with the same token. We want new intro/rend circuits to take precedence over old ones, so that HSes and clients and reestablish killed circuits without changing the HS token. */ { circuit_t *found_circ; found_circ = get_circuit_with_token(token); if (found_circ) { hs_circuitmap_remove_circuit(found_circ); if (!found_circ->marked_for_close) { circuit_mark_for_close(found_circ, END_CIRC_REASON_FINISHED); } } } /* Register circuit and token to circuitmap. */ circ->hs_token = token; HT_INSERT(hs_circuitmap_ht, the_hs_circuitmap, circ); }
/** Process a 'created' <b>cell</b> that just arrived from <b>conn</b>. * Find the circuit * that it's intended for. If we're not the origin of the circuit, package * the 'created' cell in an 'extended' relay cell and pass it back. If we * are the origin of the circuit, send it to circuit_finish_handshake() to * finish processing keys, and then call circuit_send_next_onion_skin() to * extend to the next hop in the circuit if necessary. */ static void command_process_created_cell(cell_t *cell, or_connection_t *conn) { circuit_t *circ; circ = circuit_get_by_circid_orconn(cell->circ_id, conn); if (!circ) { log_info(LD_OR, "(circID %d) unknown circ (probably got a destroy earlier). " "Dropping.", cell->circ_id); return; } if (circ->n_circ_id != cell->circ_id) { log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL, "got created cell from Tor client? Closing."); circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); return; } if (CIRCUIT_IS_ORIGIN(circ)) { /* we're the OP. Handshake this. */ origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ); int err_reason = 0; log_debug(LD_OR,"at OP. Finishing handshake."); if ((err_reason = circuit_finish_handshake(origin_circ, cell->command, cell->payload)) < 0) { log_warn(LD_OR,"circuit_finish_handshake failed."); circuit_mark_for_close(circ, -err_reason); return; } log_debug(LD_OR,"Moving to next skin."); if ((err_reason = circuit_send_next_onion_skin(origin_circ)) < 0) { log_info(LD_OR,"circuit_send_next_onion_skin failed."); /* XXX push this circuit_close lower */ circuit_mark_for_close(circ, -err_reason); return; } } else { /* pack it into an extended relay cell, and send it. */ log_debug(LD_OR, "Converting created cell to extended relay cell, sending."); relay_send_command_from_edge(0, circ, RELAY_COMMAND_EXTENDED, (char*)cell->payload, ONIONSKIN_REPLY_LEN, NULL); } }
/** Process an ESTABLISH_RENDEZVOUS cell by setting the circuit's purpose and * rendezvous cookie. */ int rend_mid_establish_rendezvous(or_circuit_t *circ, const uint8_t *request, size_t request_len) { char hexid[9]; int reason = END_CIRC_REASON_TORPROTOCOL; log_info(LD_REND, "Received an ESTABLISH_RENDEZVOUS request on circuit %u", (unsigned)circ->p_circ_id); if (circ->base_.purpose != CIRCUIT_PURPOSE_OR) { log_warn(LD_PROTOCOL, "Tried to establish rendezvous on non-OR circuit with purpose %s", circuit_purpose_to_string(circ->base_.purpose)); goto err; } if (circ->base_.n_chan) { log_warn(LD_PROTOCOL, "Tried to establish rendezvous on non-edge circuit"); goto err; } if (request_len != REND_COOKIE_LEN) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Invalid length on ESTABLISH_RENDEZVOUS."); goto err; } if (circuit_get_rendezvous(request)) { log_warn(LD_PROTOCOL, "Duplicate rendezvous cookie in ESTABLISH_RENDEZVOUS."); goto err; } /* Acknowledge the request. */ if (relay_send_command_from_edge(0,TO_CIRCUIT(circ), RELAY_COMMAND_RENDEZVOUS_ESTABLISHED, "", 0, NULL)<0) { log_warn(LD_PROTOCOL, "Couldn't send RENDEZVOUS_ESTABLISHED cell."); reason = END_CIRC_REASON_INTERNAL; goto err; } circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_REND_POINT_WAITING); circuit_set_rendezvous_cookie(circ, request); base16_encode(hexid,9,(char*)request,4); log_info(LD_REND, "Established rendezvous point on circuit %u for cookie %s", (unsigned)circ->p_circ_id, hexid); return 0; err: circuit_mark_for_close(TO_CIRCUIT(circ), reason); return -1; }
/** Add <b>circ</b> to the end of ol_list and return 0, except * if ol_list is too long, in which case do nothing and return -1. */ int onion_pending_add(or_circuit_t *circ, char *onionskin) { onion_queue_t *tmp; time_t now = time(NULL); tmp = tor_malloc_zero(sizeof(onion_queue_t)); tmp->circ = circ; tmp->onionskin = onionskin; tmp->when_added = now; if (!ol_tail) { tor_assert(!ol_list); tor_assert(!ol_length); ol_list = tmp; ol_tail = tmp; ol_length++; return 0; } tor_assert(ol_list); tor_assert(!ol_tail->next); if (ol_length >= get_options()->MaxOnionsPending) { #define WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL (60) static ratelim_t last_warned = RATELIM_INIT(WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL); char *m; if ((m = rate_limit_log(&last_warned, approx_time()))) { log_warn(LD_GENERAL, "Your computer is too slow to handle this many circuit " "creation requests! Please consider using the " "MaxAdvertisedBandwidth config option or choosing a more " "restricted exit policy.%s",m); tor_free(m); } tor_free(tmp); return -1; } ol_length++; ol_tail->next = tmp; ol_tail = tmp; while ((int)(now - ol_list->when_added) >= ONIONQUEUE_WAIT_CUTOFF) { /* cull elderly requests. */ circ = ol_list->circ; onion_pending_remove(ol_list->circ); log_info(LD_CIRC, "Circuit create request is too old; canceling due to overload."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT); } return 0; }
/** Go through the circuitlist; mark-for-close each circuit that starts * at us but has not yet been used. */ void circuit_mark_all_unused_circs(void) { circuit_t *circ; for (circ=global_circuitlist; circ; circ = circ->next) { if (CIRCUIT_IS_ORIGIN(circ) && !circ->marked_for_close && !circ->timestamp_dirty) circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); } }
/** Process an ESTABLISH_RENDEZVOUS cell by setting the circuit's purpose and * rendezvous cookie. */ int rend_mid_establish_rendezvous(or_circuit_t *circ, const char *request, size_t request_len) { char hexid[9]; int reason = END_CIRC_REASON_TORPROTOCOL; log_info(LD_REND, "Received an ESTABLISH_RENDEZVOUS request on circuit %d", circ->p_circ_id); if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_conn) { log_warn(LD_PROTOCOL, "Tried to establish rendezvous on non-OR or non-edge circuit."); goto err; } if (request_len != REND_COOKIE_LEN) { log_warn(LD_PROTOCOL, "Invalid length on ESTABLISH_RENDEZVOUS."); goto err; } if (circuit_get_rendezvous(request)) { log_warn(LD_PROTOCOL, "Duplicate rendezvous cookie in ESTABLISH_RENDEZVOUS."); goto err; } /* Acknowledge the request. */ if (relay_send_command_from_edge(0,TO_CIRCUIT(circ), RELAY_COMMAND_RENDEZVOUS_ESTABLISHED, "", 0, NULL)<0) { log_warn(LD_PROTOCOL, "Couldn't send RENDEZVOUS_ESTABLISHED cell."); reason = END_CIRC_REASON_INTERNAL; goto err; } circ->_base.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; memcpy(circ->rend_token, request, REND_COOKIE_LEN); base16_encode(hexid,9,request,4); log_info(LD_REND, "Established rendezvous point on circuit %d for cookie %s", circ->p_circ_id, hexid); return 0; err: circuit_mark_for_close(TO_CIRCUIT(circ), reason); return -1; }
/* For a given introduction point and an introduction circuit, send the * ESTABLISH_INTRO cell. The service object is used for logging. This can fail * and if so, the circuit is closed and the intro point object is flagged * that the circuit is not established anymore which is important for the * retry mechanism. */ static void send_establish_intro(const hs_service_t *service, hs_service_intro_point_t *ip, origin_circuit_t *circ) { ssize_t cell_len; uint8_t payload[RELAY_PAYLOAD_SIZE]; tor_assert(service); tor_assert(ip); tor_assert(circ); /* Encode establish intro cell. */ cell_len = hs_cell_build_establish_intro(circ->cpath->prev->rend_circ_nonce, ip, payload); if (cell_len < 0) { log_warn(LD_REND, "Unable to encode ESTABLISH_INTRO cell for service %s " "on circuit %u. Closing circuit.", safe_str_client(service->onion_address), TO_CIRCUIT(circ)->n_circ_id); goto err; } /* Send the cell on the circuit. */ if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(circ), RELAY_COMMAND_ESTABLISH_INTRO, (char *) payload, cell_len, circ->cpath->prev) < 0) { log_info(LD_REND, "Unable to send ESTABLISH_INTRO cell for service %s " "on circuit %u.", safe_str_client(service->onion_address), TO_CIRCUIT(circ)->n_circ_id); /* On error, the circuit has been closed. */ goto done; } /* Record the attempt to use this circuit. */ pathbias_count_use_attempt(circ); goto done; err: circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); done: memwipe(payload, 0, sizeof(payload)); }
/** Send the establish-rendezvous cell along a rendezvous circuit. if * it fails, mark the circ for close and return -1. else return 0. */ static int rend_client_send_establish_rendezvous(origin_circuit_t *circ) { tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND); tor_assert(circ->rend_data); log_info(LD_REND, "Sending an ESTABLISH_RENDEZVOUS cell"); if (crypto_rand(circ->rend_data->rend_cookie, REND_COOKIE_LEN) < 0) { log_warn(LD_BUG, "Internal error: Couldn't produce random cookie."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return -1; } if (relay_send_command_from_edge(0, TO_CIRCUIT(circ), RELAY_COMMAND_ESTABLISH_RENDEZVOUS, circ->rend_data->rend_cookie, REND_COOKIE_LEN, circ->cpath->prev)<0) { /* circ is already marked for close */ log_warn(LD_GENERAL, "Couldn't send ESTABLISH_RENDEZVOUS cell"); return -1; } return 0; }
/** For each circuit that has <b>conn</b> as n_conn or p_conn, unlink the * circuit from the orconn,circid map, and mark it for close if it hasn't * been marked already. */ void circuit_unlink_all_from_or_conn(or_connection_t *conn, int reason) { circuit_t *circ; connection_or_unlink_all_active_circs(conn); for (circ = global_circuitlist; circ; circ = circ->next) { int mark = 0; if (circ->n_conn == conn) { circuit_set_n_circid_orconn(circ, 0, NULL); mark = 1; } if (! CIRCUIT_IS_ORIGIN(circ)) { or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); if (or_circ->p_conn == conn) { circuit_set_p_circid_orconn(or_circ, 0, NULL); mark = 1; } } if (mark && !circ->marked_for_close) circuit_mark_for_close(circ, reason); } }
/** Called when we get data from a cpuworker. If the answer is not complete, * wait for a complete answer. If the answer is complete, * process it as appropriate. */ int connection_cpu_process_inbuf(connection_t *conn) { uint64_t chan_id; circid_t circ_id; channel_t *p_chan = NULL; circuit_t *circ; tor_assert(conn); tor_assert(conn->type == CONN_TYPE_CPUWORKER); if (!connection_get_inbuf_len(conn)) return 0; if (conn->state == CPUWORKER_STATE_BUSY_ONION) { cpuworker_reply_t rpl; if (connection_get_inbuf_len(conn) < sizeof(cpuworker_reply_t)) return 0; /* not yet */ tor_assert(connection_get_inbuf_len(conn) == sizeof(cpuworker_reply_t)); connection_fetch_from_buf((void*)&rpl,sizeof(cpuworker_reply_t),conn); tor_assert(rpl.magic == CPUWORKER_REPLY_MAGIC); if (rpl.timed && rpl.success && rpl.handshake_type <= MAX_ONION_HANDSHAKE_TYPE) { /* Time how long this request took. The handshake_type check should be needless, but let's leave it in to be safe. */ struct timeval tv_end, tv_diff; int64_t usec_roundtrip; tor_gettimeofday(&tv_end); timersub(&tv_end, &rpl.started_at, &tv_diff); usec_roundtrip = ((int64_t)tv_diff.tv_sec)*1000000 + tv_diff.tv_usec; if (usec_roundtrip >= 0 && usec_roundtrip < MAX_BELIEVABLE_ONIONSKIN_DELAY) { ++onionskins_n_processed[rpl.handshake_type]; onionskins_usec_internal[rpl.handshake_type] += rpl.n_usec; onionskins_usec_roundtrip[rpl.handshake_type] += usec_roundtrip; if (onionskins_n_processed[rpl.handshake_type] >= 500000) { /* Scale down every 500000 handshakes. On a busy server, that's * less impressive than it sounds. */ onionskins_n_processed[rpl.handshake_type] /= 2; onionskins_usec_internal[rpl.handshake_type] /= 2; onionskins_usec_roundtrip[rpl.handshake_type] /= 2; } } } /* parse out the circ it was talking about */ tag_unpack(rpl.tag, &chan_id, &circ_id); circ = NULL; log_debug(LD_OR, "Unpacking cpuworker reply, chan_id is " U64_FORMAT ", circ_id is %u", U64_PRINTF_ARG(chan_id), (unsigned)circ_id); p_chan = channel_find_by_global_id(chan_id); if (p_chan) circ = circuit_get_by_circid_channel(circ_id, p_chan); if (rpl.success == 0) { log_debug(LD_OR, "decoding onionskin failed. " "(Old key or bad software.) Closing."); if (circ) circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); goto done_processing; } if (!circ) { /* This happens because somebody sends us a destroy cell and the * circuit goes away, while the cpuworker is working. This is also * why our tag doesn't include a pointer to the circ, because we'd * never know if it's still valid. */ log_debug(LD_OR,"processed onion for a circ that's gone. Dropping."); goto done_processing; } tor_assert(! CIRCUIT_IS_ORIGIN(circ)); if (onionskin_answer(TO_OR_CIRCUIT(circ), &rpl.created_cell, (const char*)rpl.keys, rpl.rend_auth_material) < 0) { log_warn(LD_OR,"onionskin_answer failed. Closing."); circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL); goto done_processing; } log_debug(LD_OR,"onionskin_answer succeeded. Yay."); } else { tor_assert(0); /* don't ask me to do handshakes yet */ } done_processing: conn->state = CPUWORKER_STATE_IDLE; num_cpuworkers_busy--; if (conn->timestamp_created < last_rotation_time) { connection_mark_for_close(conn); num_cpuworkers--; spawn_enough_cpuworkers(); } else { process_pending_task(conn); } return 0; }
/** Called when we get data from a cpuworker. If the answer is not complete, * wait for a complete answer. If the answer is complete, * process it as appropriate. */ int connection_cpu_process_inbuf(connection_t *conn) { char success; char buf[LEN_ONION_RESPONSE]; uint64_t conn_id; circid_t circ_id; connection_t *tmp_conn; or_connection_t *p_conn = NULL; circuit_t *circ; tor_assert(conn); tor_assert(conn->type == CONN_TYPE_CPUWORKER); if (!buf_datalen(conn->inbuf)) return 0; if (conn->state == CPUWORKER_STATE_BUSY_ONION) { if (buf_datalen(conn->inbuf) < LEN_ONION_RESPONSE) /* answer available? */ return 0; /* not yet */ tor_assert(buf_datalen(conn->inbuf) == LEN_ONION_RESPONSE); connection_fetch_from_buf(&success,1,conn); connection_fetch_from_buf(buf,LEN_ONION_RESPONSE-1,conn); /* parse out the circ it was talking about */ tag_unpack(buf, &conn_id, &circ_id); circ = NULL; tmp_conn = connection_get_by_global_id(conn_id); if (tmp_conn && !tmp_conn->marked_for_close && tmp_conn->type == CONN_TYPE_OR) p_conn = TO_OR_CONN(tmp_conn); if (p_conn) circ = circuit_get_by_circid_orconn(circ_id, p_conn); if (success == 0) { log_debug(LD_OR, "decoding onionskin failed. " "(Old key or bad software.) Closing."); if (circ) circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); goto done_processing; } if (!circ) { /* This happens because somebody sends us a destroy cell and the * circuit goes away, while the cpuworker is working. This is also * why our tag doesn't include a pointer to the circ, because we'd * never know if it's still valid. */ log_debug(LD_OR,"processed onion for a circ that's gone. Dropping."); goto done_processing; } tor_assert(! CIRCUIT_IS_ORIGIN(circ)); if (onionskin_answer(TO_OR_CIRCUIT(circ), CELL_CREATED, buf+TAG_LEN, buf+TAG_LEN+ONIONSKIN_REPLY_LEN) < 0) { log_warn(LD_OR,"onionskin_answer failed. Closing."); circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL); goto done_processing; } log_debug(LD_OR,"onionskin_answer succeeded. Yay."); } else { tor_assert(0); /* don't ask me to do handshakes yet */ } done_processing: conn->state = CPUWORKER_STATE_IDLE; num_cpuworkers_busy--; if (conn->timestamp_created < last_rotation_time) { connection_mark_for_close(conn); num_cpuworkers--; spawn_enough_cpuworkers(); } else { process_pending_task(conn); } return 0; }
/** Process a RENDEZVOUS1 cell by looking up the correct rendezvous * circuit by its relaying the cell's body in a RENDEZVOUS2 cell, and * connecting the two circuits. */ int rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request, size_t request_len) { or_circuit_t *rend_circ; char hexid[9]; int reason = END_CIRC_REASON_INTERNAL; if (circ->base_.purpose != CIRCUIT_PURPOSE_OR || circ->base_.n_chan) { log_info(LD_REND, "Tried to complete rendezvous on non-OR or non-edge circuit %u.", (unsigned)circ->p_circ_id); reason = END_CIRC_REASON_TORPROTOCOL; goto err; } if (request_len != REND_COOKIE_LEN+DH_KEY_LEN+DIGEST_LEN) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Rejecting RENDEZVOUS1 cell with bad length (%d) on circuit %u.", (int)request_len, (unsigned)circ->p_circ_id); reason = END_CIRC_REASON_TORPROTOCOL; goto err; } base16_encode(hexid, sizeof(hexid), (const char*)request, 4); log_info(LD_REND, "Got request for rendezvous from circuit %u to cookie %s.", (unsigned)circ->p_circ_id, hexid); rend_circ = circuit_get_rendezvous(request); if (!rend_circ) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Rejecting RENDEZVOUS1 cell with unrecognized rendezvous cookie %s.", hexid); reason = END_CIRC_REASON_TORPROTOCOL; goto err; } /* Send the RENDEZVOUS2 cell to Alice. */ if (relay_send_command_from_edge(0, TO_CIRCUIT(rend_circ), RELAY_COMMAND_RENDEZVOUS2, (char*)(request+REND_COOKIE_LEN), request_len-REND_COOKIE_LEN, NULL)) { log_warn(LD_GENERAL, "Unable to send RENDEZVOUS2 cell to client on circuit %u.", (unsigned)rend_circ->p_circ_id); goto err; } /* Join the circuits. */ log_info(LD_REND, "Completing rendezvous: circuit %u joins circuit %u (cookie %s)", (unsigned)circ->p_circ_id, (unsigned)rend_circ->p_circ_id, hexid); circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_REND_ESTABLISHED); circuit_change_purpose(TO_CIRCUIT(rend_circ), CIRCUIT_PURPOSE_REND_ESTABLISHED); circuit_set_rendezvous_cookie(circ, NULL); rend_circ->rend_splice = circ; circ->rend_splice = rend_circ; return 0; err: circuit_mark_for_close(TO_CIRCUIT(circ), reason); return -1; }
/* For a given service, the ntor onion key and a rendezvous cookie, launch a * circuit to the rendezvous point specified by the link specifiers. On * success, a circuit identifier is attached to the circuit with the needed * data. This function will try to open a circuit for a maximum value of * MAX_REND_FAILURES then it will give up. */ static void launch_rendezvous_point_circuit(const hs_service_t *service, const hs_service_intro_point_t *ip, const hs_cell_introduce2_data_t *data) { int circ_needs_uptime; time_t now = time(NULL); extend_info_t *info = NULL; origin_circuit_t *circ; tor_assert(service); tor_assert(ip); tor_assert(data); circ_needs_uptime = hs_service_requires_uptime_circ(service->config.ports); /* Get the extend info data structure for the chosen rendezvous point * specified by the given link specifiers. */ info = hs_get_extend_info_from_lspecs(data->link_specifiers, &data->onion_pk, service->config.is_single_onion); if (info == NULL) { /* We are done here, we can't extend to the rendezvous point. * If you're running an IPv6-only v3 single onion service on 0.3.2 or with * 0.3.2 clients, and somehow disable the option check, it will fail here. */ log_fn(LOG_PROTOCOL_WARN, LD_REND, "Not enough info to open a circuit to a rendezvous point for " "%s service %s.", get_service_anonymity_string(service), safe_str_client(service->onion_address)); goto end; } for (int i = 0; i < MAX_REND_FAILURES; i++) { int circ_flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL; if (circ_needs_uptime) { circ_flags |= CIRCLAUNCH_NEED_UPTIME; } /* Firewall and policies are checked when getting the extend info. */ if (service->config.is_single_onion) { circ_flags |= CIRCLAUNCH_ONEHOP_TUNNEL; } circ = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_CONNECT_REND, info, circ_flags); if (circ != NULL) { /* Stop retrying, we have a circuit! */ break; } } if (circ == NULL) { log_warn(LD_REND, "Giving up on launching a rendezvous circuit to %s " "for %s service %s", safe_str_client(extend_info_describe(info)), get_service_anonymity_string(service), safe_str_client(service->onion_address)); goto end; } log_info(LD_REND, "Rendezvous circuit launched to %s with cookie %s " "for %s service %s", safe_str_client(extend_info_describe(info)), safe_str_client(hex_str((const char *) data->rendezvous_cookie, REND_COOKIE_LEN)), get_service_anonymity_string(service), safe_str_client(service->onion_address)); tor_assert(circ->build_state); /* Rendezvous circuit have a specific timeout for the time spent on trying * to connect to the rendezvous point. */ circ->build_state->expiry_time = now + MAX_REND_TIMEOUT; /* Create circuit identifier and key material. */ { hs_ntor_rend_cell_keys_t keys; curve25519_keypair_t ephemeral_kp; /* No need for extra strong, this is only for this circuit life time. This * key will be used for the RENDEZVOUS1 cell that will be sent on the * circuit once opened. */ curve25519_keypair_generate(&ephemeral_kp, 0); if (hs_ntor_service_get_rendezvous1_keys(&ip->auth_key_kp.pubkey, &ip->enc_key_kp, &ephemeral_kp, &data->client_pk, &keys) < 0) { /* This should not really happened but just in case, don't make tor * freak out, close the circuit and move on. */ log_info(LD_REND, "Unable to get RENDEZVOUS1 key material for " "service %s", safe_str_client(service->onion_address)); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); goto end; } circ->hs_ident = create_rp_circuit_identifier(service, data->rendezvous_cookie, &ephemeral_kp.pubkey, &keys); memwipe(&ephemeral_kp, 0, sizeof(ephemeral_kp)); memwipe(&keys, 0, sizeof(keys)); tor_assert(circ->hs_ident); } end: extend_info_free(info); }
/** Mark <b>circ</b> to be closed next time we call * circuit_close_all_marked(). Do any cleanup needed: * - If state is onionskin_pending, remove circ from the onion_pending * list. * - If circ isn't open yet: call circuit_build_failed() if we're * the origin, and in either case call circuit_rep_hist_note_result() * to note stats. * - If purpose is C_INTRODUCE_ACK_WAIT, remove the intro point we * just tried from our list of intro points for that service * descriptor. * - Send appropriate destroys and edge_destroys for conns and * streams attached to circ. * - If circ->rend_splice is set (we are the midpoint of a joined * rendezvous stream), then mark the other circuit to close as well. */ void _circuit_mark_for_close(circuit_t *circ, int reason, int line, const char *file) { int orig_reason = reason; /* Passed to the controller */ assert_circuit_ok(circ); tor_assert(line); tor_assert(file); if (circ->marked_for_close) { log(LOG_WARN,LD_BUG, "Duplicate call to circuit_mark_for_close at %s:%d" " (first at %s:%d)", file, line, circ->marked_for_close_file, circ->marked_for_close); return; } if (reason == END_CIRC_AT_ORIGIN) { if (!CIRCUIT_IS_ORIGIN(circ)) { log_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; } if (CIRCUIT_IS_ORIGIN(circ)) { /* We don't send reasons when closing circuits at the origin. */ reason = END_CIRC_REASON_NONE; } if (reason & END_CIRC_REASON_FLAG_REMOTE) reason &= ~END_CIRC_REASON_FLAG_REMOTE; if (reason < _END_CIRC_REASON_MIN || reason > _END_CIRC_REASON_MAX) { if (!(orig_reason & END_CIRC_REASON_FLAG_REMOTE)) log_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(TO_OR_CIRCUIT(circ)); } /* If the circuit ever became OPEN, we sent it to the reputation history * module then. If it isn't OPEN, we send it there now to remember which * links worked and which didn't. */ if (circ->state != CIRCUIT_STATE_OPEN) { if (CIRCUIT_IS_ORIGIN(circ)) { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); circuit_build_failed(ocirc); /* take actions if necessary */ circuit_rep_hist_note_result(ocirc); } } if (circ->state == CIRCUIT_STATE_OR_WAIT) { if (circuits_pending_or_conns) smartlist_remove(circuits_pending_or_conns, circ); } if (CIRCUIT_IS_ORIGIN(circ)) { control_event_circuit_status(TO_ORIGIN_CIRCUIT(circ), (circ->state == CIRCUIT_STATE_OPEN)?CIRC_EVENT_CLOSED:CIRC_EVENT_FAILED, orig_reason); } if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); tor_assert(circ->state == CIRCUIT_STATE_OPEN); tor_assert(ocirc->build_state->chosen_exit); tor_assert(ocirc->rend_data); /* treat this like getting a nack from it */ log_info(LD_REND, "Failed intro circ %s to %s (awaiting ack). " "Removing from descriptor.", safe_str(ocirc->rend_data->onion_address), safe_str(build_state_get_exit_nickname(ocirc->build_state))); rend_client_remove_intro_point(ocirc->build_state->chosen_exit, ocirc->rend_data); } if (circ->n_conn) connection_or_send_destroy(circ->n_circ_id, circ->n_conn, reason); if (! CIRCUIT_IS_ORIGIN(circ)) { or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); edge_connection_t *conn; for (conn=or_circ->n_streams; conn; conn=conn->next_stream) connection_edge_destroy(or_circ->p_circ_id, conn); while (or_circ->resolving_streams) { conn = or_circ->resolving_streams; or_circ->resolving_streams = conn->next_stream; if (!conn->_base.marked_for_close) { /* The client will see a DESTROY, and infer that the connections * are closing because the circuit is getting torn down. No need * to send an end cell. */ conn->edge_has_sent_end = 1; conn->end_reason = END_STREAM_REASON_DESTROY; conn->end_reason |= END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED; connection_mark_for_close(TO_CONN(conn)); } conn->on_circuit = NULL; } if (or_circ->p_conn) connection_or_send_destroy(or_circ->p_circ_id, or_circ->p_conn, reason); } else { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); edge_connection_t *conn; for (conn=ocirc->p_streams; conn; conn=conn->next_stream) connection_edge_destroy(circ->n_circ_id, conn); } circ->marked_for_close = line; circ->marked_for_close_file = file; if (!CIRCUIT_IS_ORIGIN(circ)) { or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); if (or_circ->rend_splice) { if (!or_circ->rend_splice->_base.marked_for_close) { /* do this after marking this circuit, to avoid infinite recursion. */ circuit_mark_for_close(TO_CIRCUIT(or_circ->rend_splice), reason); } or_circ->rend_splice = NULL; } } }
/** Process a 'create' <b>cell</b> that just arrived from <b>conn</b>. Make a * new circuit with the p_circ_id specified in cell. Put the circuit in state * onionskin_pending, and pass the onionskin to the cpuworker. Circ will get * picked up again when the cpuworker finishes decrypting it. */ static void command_process_create_cell(cell_t *cell, or_connection_t *conn) { or_circuit_t *circ; int id_is_high; if (we_are_hibernating()) { log_info(LD_OR, "Received create cell but we're shutting down. Sending back " "destroy."); connection_or_send_destroy(cell->circ_id, conn, END_CIRC_REASON_HIBERNATING); return; } if (!server_mode(get_options())) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received create cell (type %d) from %s:%d, but we're a client. " "Sending back a destroy.", (int)cell->command, conn->_base.address, conn->_base.port); connection_or_send_destroy(cell->circ_id, conn, END_CIRC_REASON_TORPROTOCOL); return; } /* If the high bit of the circuit ID is not as expected, close the * circ. */ id_is_high = cell->circ_id & (1<<15); if ((id_is_high && conn->circ_id_type == CIRC_ID_TYPE_HIGHER) || (!id_is_high && conn->circ_id_type == CIRC_ID_TYPE_LOWER)) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received create cell with unexpected circ_id %d. Closing.", cell->circ_id); connection_or_send_destroy(cell->circ_id, conn, END_CIRC_REASON_TORPROTOCOL); return; } if (circuit_id_in_use_on_orconn(cell->circ_id, conn)) { routerinfo_t *router = router_get_by_digest(conn->identity_digest); log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received CREATE cell (circID %d) for known circ. " "Dropping (age %d).", cell->circ_id, (int)(time(NULL) - conn->_base.timestamp_created)); if (router) log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Details: nickname \"%s\", platform %s.", router->nickname, escaped(router->platform)); return; } circ = or_circuit_new(cell->circ_id, conn); circ->_base.purpose = CIRCUIT_PURPOSE_OR; circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_ONIONSKIN_PENDING); if (cell->command == CELL_CREATE) { char *onionskin = tor_malloc(ONIONSKIN_CHALLENGE_LEN); memcpy(onionskin, cell->payload, ONIONSKIN_CHALLENGE_LEN); /* hand it off to the cpuworkers, and then return. */ if (assign_onionskin_to_cpuworker(NULL, circ, onionskin) < 0) { log_warn(LD_GENERAL,"Failed to hand off onionskin. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return; } log_debug(LD_OR,"success: handed off onionskin."); } else { /* This is a CREATE_FAST cell; we can handle it immediately without using * a CPU worker. */ char keys[CPATH_KEY_MATERIAL_LEN]; char reply[DIGEST_LEN*2]; tor_assert(cell->command == CELL_CREATE_FAST); if (fast_server_handshake(cell->payload, reply, keys, sizeof(keys))<0) { log_warn(LD_OR,"Failed to generate key material. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return; } if (onionskin_answer(circ, CELL_CREATED_FAST, reply, keys)<0) { log_warn(LD_OR,"Failed to reply to CREATE_FAST cell. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return; } } }
/** Process a 'create' <b>cell</b> that just arrived from <b>chan</b>. Make a * new circuit with the p_circ_id specified in cell. Put the circuit in state * onionskin_pending, and pass the onionskin to the cpuworker. Circ will get * picked up again when the cpuworker finishes decrypting it. */ static void command_process_create_cell(cell_t *cell, channel_t *chan) { or_circuit_t *circ; const or_options_t *options = get_options(); int id_is_high; create_cell_t *create_cell; tor_assert(cell); tor_assert(chan); log_debug(LD_OR, "Got a CREATE cell for circ_id %u on channel " U64_FORMAT " (%p)", (unsigned)cell->circ_id, U64_PRINTF_ARG(chan->global_identifier), chan); if (we_are_hibernating()) { log_info(LD_OR, "Received create cell but we're shutting down. Sending back " "destroy."); channel_send_destroy(cell->circ_id, chan, END_CIRC_REASON_HIBERNATING); return; } if (!server_mode(options) || (!public_server_mode(options) && channel_is_outgoing(chan))) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received create cell (type %d) from %s, but we're connected " "to it as a client. " "Sending back a destroy.", (int)cell->command, channel_get_canonical_remote_descr(chan)); channel_send_destroy(cell->circ_id, chan, END_CIRC_REASON_TORPROTOCOL); return; } if (cell->circ_id == 0) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received a create cell (type %d) from %s with zero circID; " " ignoring.", (int)cell->command, channel_get_actual_remote_descr(chan)); return; } /* If the high bit of the circuit ID is not as expected, close the * circ. */ if (chan->wide_circ_ids) id_is_high = cell->circ_id & (1u<<31); else id_is_high = cell->circ_id & (1u<<15); if ((id_is_high && chan->circ_id_type == CIRC_ID_TYPE_HIGHER) || (!id_is_high && chan->circ_id_type == CIRC_ID_TYPE_LOWER)) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received create cell with unexpected circ_id %u. Closing.", (unsigned)cell->circ_id); channel_send_destroy(cell->circ_id, chan, END_CIRC_REASON_TORPROTOCOL); return; } if (circuit_id_in_use_on_channel(cell->circ_id, chan)) { const node_t *node = node_get_by_id(chan->identity_digest); log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received CREATE cell (circID %u) for known circ. " "Dropping (age %d).", (unsigned)cell->circ_id, (int)(time(NULL) - channel_when_created(chan))); if (node) { char *p = esc_for_log(node_get_platform(node)); log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Details: router %s, platform %s.", node_describe(node), p); tor_free(p); } return; } circ = or_circuit_new(cell->circ_id, chan); circ->base_.purpose = CIRCUIT_PURPOSE_OR; circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_ONIONSKIN_PENDING); create_cell = tor_malloc_zero(sizeof(create_cell_t)); if (create_cell_parse(create_cell, cell) < 0) { tor_free(create_cell); log_fn(LOG_PROTOCOL_WARN, LD_OR, "Bogus/unrecognized create cell; closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL); return; } if (create_cell->handshake_type != ONION_HANDSHAKE_TYPE_FAST) { /* hand it off to the cpuworkers, and then return. */ if (connection_or_digest_is_known_relay(chan->identity_digest)) rep_hist_note_circuit_handshake_requested(create_cell->handshake_type); if (assign_onionskin_to_cpuworker(NULL, circ, create_cell) < 0) { log_debug(LD_GENERAL,"Failed to hand off onionskin. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT); return; } log_debug(LD_OR,"success: handed off onionskin."); } else { /* This is a CREATE_FAST cell; we can handle it immediately without using * a CPU worker. */ uint8_t keys[CPATH_KEY_MATERIAL_LEN]; uint8_t rend_circ_nonce[DIGEST_LEN]; int len; created_cell_t created_cell; /* Make sure we never try to use the OR connection on which we * received this cell to satisfy an EXTEND request, */ channel_mark_client(chan); memset(&created_cell, 0, sizeof(created_cell)); len = onion_skin_server_handshake(ONION_HANDSHAKE_TYPE_FAST, create_cell->onionskin, create_cell->handshake_len, NULL, created_cell.reply, keys, CPATH_KEY_MATERIAL_LEN, rend_circ_nonce); tor_free(create_cell); if (len < 0) { log_warn(LD_OR,"Failed to generate key material. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); tor_free(create_cell); return; } created_cell.cell_type = CELL_CREATED_FAST; created_cell.handshake_len = len; if (onionskin_answer(circ, &created_cell, (const char *)keys, rend_circ_nonce)<0) { log_warn(LD_OR,"Failed to reply to CREATE_FAST cell. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return; } memwipe(keys, 0, sizeof(keys)); } }
static void test_relay_close_circuit(void *arg) { channel_t *nchan = NULL, *pchan = NULL; or_circuit_t *orcirc = NULL; cell_t *cell = NULL; int old_count, new_count; (void)arg; /* Make fake channels to be nchan and pchan for the circuit */ nchan = new_fake_channel(); tt_assert(nchan); pchan = new_fake_channel(); tt_assert(pchan); /* Make a fake orcirc */ orcirc = new_fake_orcirc(nchan, pchan); tt_assert(orcirc); circuitmux_attach_circuit(nchan->cmux, TO_CIRCUIT(orcirc), CELL_DIRECTION_OUT); circuitmux_attach_circuit(pchan->cmux, TO_CIRCUIT(orcirc), CELL_DIRECTION_IN); /* Make a cell */ cell = tor_malloc_zero(sizeof(cell_t)); make_fake_cell(cell); MOCK(scheduler_channel_has_waiting_cells, scheduler_channel_has_waiting_cells_mock); MOCK(assert_circuit_ok, assert_circuit_ok_mock); /* Append it */ old_count = get_mock_scheduler_has_waiting_cells_count(); append_cell_to_circuit_queue(TO_CIRCUIT(orcirc), nchan, cell, CELL_DIRECTION_OUT, 0); new_count = get_mock_scheduler_has_waiting_cells_count(); tt_int_op(new_count, OP_EQ, old_count + 1); /* Now try the reverse direction */ old_count = get_mock_scheduler_has_waiting_cells_count(); append_cell_to_circuit_queue(TO_CIRCUIT(orcirc), pchan, cell, CELL_DIRECTION_IN, 0); new_count = get_mock_scheduler_has_waiting_cells_count(); tt_int_op(new_count, OP_EQ, old_count + 1); /* Ensure our write totals are 0 */ tt_u64_op(find_largest_max(write_array), OP_EQ, 0); /* Mark the circuit for close */ circuit_mark_for_close(TO_CIRCUIT(orcirc), 0); /* Check our write totals. */ advance_obs(write_array); commit_max(write_array); /* Check for two cells plus overhead */ tt_u64_op(find_largest_max(write_array), OP_EQ, 2*(get_cell_network_size(nchan->wide_circ_ids) +TLS_PER_CELL_OVERHEAD)); UNMOCK(scheduler_channel_has_waiting_cells); /* Get rid of the fake channels */ MOCK(scheduler_release_channel, scheduler_release_channel_mock); channel_mark_for_close(nchan); channel_mark_for_close(pchan); UNMOCK(scheduler_release_channel); /* Shut down channels */ channel_free_all(); done: tor_free(cell); if (orcirc) { circuitmux_detach_circuit(nchan->cmux, TO_CIRCUIT(orcirc)); circuitmux_detach_circuit(pchan->cmux, TO_CIRCUIT(orcirc)); cell_queue_clear(&orcirc->base_.n_chan_cells); cell_queue_clear(&orcirc->p_chan_cells); } tor_free(orcirc); free_fake_channel(nchan); free_fake_channel(pchan); UNMOCK(assert_circuit_ok); return; }
static void test_rend_token_maps(void *arg) { or_circuit_t *c1, *c2, *c3, *c4; const uint8_t tok1[REND_TOKEN_LEN] = "The cat can't tell y"; const uint8_t tok2[REND_TOKEN_LEN] = "ou its name, and it "; const uint8_t tok3[REND_TOKEN_LEN] = "doesn't really care."; /* -- Adapted from a quote by Fredrik Lundh. */ (void)arg; (void)tok1; //xxxx c1 = or_circuit_new(0, NULL); c2 = or_circuit_new(0, NULL); c3 = or_circuit_new(0, NULL); c4 = or_circuit_new(0, NULL); /* Make sure we really filled up the tok* variables */ tt_int_op(tok1[REND_TOKEN_LEN-1], OP_EQ, 'y'); tt_int_op(tok2[REND_TOKEN_LEN-1], OP_EQ, ' '); tt_int_op(tok3[REND_TOKEN_LEN-1], OP_EQ, '.'); /* No maps; nothing there. */ tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1)); tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok1)); circuit_set_rendezvous_cookie(c1, tok1); circuit_set_intro_point_digest(c2, tok2); tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok3)); tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok3)); tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok2)); tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok1)); /* Without purpose set, we don't get the circuits */ tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1)); tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok2)); c1->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; c2->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; /* Okay, make sure they show up now. */ tt_ptr_op(c1, OP_EQ, circuit_get_rendezvous(tok1)); tt_ptr_op(c2, OP_EQ, circuit_get_intro_point(tok2)); /* Two items at the same place with the same token. */ c3->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; circuit_set_rendezvous_cookie(c3, tok2); tt_ptr_op(c2, OP_EQ, circuit_get_intro_point(tok2)); tt_ptr_op(c3, OP_EQ, circuit_get_rendezvous(tok2)); /* Marking a circuit makes it not get returned any more */ circuit_mark_for_close(TO_CIRCUIT(c1), END_CIRC_REASON_FINISHED); tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1)); circuit_free(TO_CIRCUIT(c1)); c1 = NULL; /* Freeing a circuit makes it not get returned any more. */ circuit_free(TO_CIRCUIT(c2)); c2 = NULL; tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok2)); /* c3 -- are you still there? */ tt_ptr_op(c3, OP_EQ, circuit_get_rendezvous(tok2)); /* Change its cookie. This never happens in Tor per se, but hey. */ c3->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; circuit_set_intro_point_digest(c3, tok3); tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok2)); tt_ptr_op(c3, OP_EQ, circuit_get_intro_point(tok3)); /* Now replace c3 with c4. */ c4->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; circuit_set_intro_point_digest(c4, tok3); tt_ptr_op(c4, OP_EQ, circuit_get_intro_point(tok3)); tt_ptr_op(c3->rendinfo, OP_EQ, NULL); tt_ptr_op(c4->rendinfo, OP_NE, NULL); tt_mem_op(c4->rendinfo, OP_EQ, tok3, REND_TOKEN_LEN); /* Now clear c4's cookie. */ circuit_set_intro_point_digest(c4, NULL); tt_ptr_op(c4->rendinfo, OP_EQ, NULL); tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok3)); done: if (c1) circuit_free(TO_CIRCUIT(c1)); if (c2) circuit_free(TO_CIRCUIT(c2)); if (c3) circuit_free(TO_CIRCUIT(c3)); if (c4) circuit_free(TO_CIRCUIT(c4)); }
/** Process an INTRODUCE1 cell by finding the corresponding introduction * circuit, and relaying the body of the INTRODUCE1 cell inside an * INTRODUCE2 cell. */ int rend_mid_introduce(or_circuit_t *circ, const uint8_t *request, size_t request_len) { or_circuit_t *intro_circ; char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; char nak_body[1]; log_info(LD_REND, "Received an INTRODUCE1 request on circuit %u", (unsigned)circ->p_circ_id); if (circ->base_.purpose != CIRCUIT_PURPOSE_OR || circ->base_.n_chan) { log_warn(LD_PROTOCOL, "Rejecting INTRODUCE1 on non-OR or non-edge circuit %u.", (unsigned)circ->p_circ_id); goto err; } /* We could change this to MAX_HEX_NICKNAME_LEN now that 0.0.9.x is * obsolete; however, there isn't much reason to do so, and we're going * to revise this protocol anyway. */ if (request_len < (DIGEST_LEN+(MAX_NICKNAME_LEN+1)+REND_COOKIE_LEN+ DH_KEY_LEN+CIPHER_KEY_LEN+PKCS1_OAEP_PADDING_OVERHEAD)) { log_warn(LD_PROTOCOL, "Impossibly short INTRODUCE1 cell on circuit %u; " "responding with nack.", (unsigned)circ->p_circ_id); goto err; } base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, (char*)request, REND_SERVICE_ID_LEN); /* The first 20 bytes are all we look at: they have a hash of Bob's PK. */ intro_circ = circuit_get_intro_point((const uint8_t*)request); if (!intro_circ) { log_info(LD_REND, "No intro circ found for INTRODUCE1 cell (%s) from circuit %u; " "responding with nack.", safe_str(serviceid), (unsigned)circ->p_circ_id); goto err; } log_info(LD_REND, "Sending introduction request for service %s " "from circ %u to circ %u", safe_str(serviceid), (unsigned)circ->p_circ_id, (unsigned)intro_circ->p_circ_id); /* Great. Now we just relay the cell down the circuit. */ if (relay_send_command_from_edge(0, TO_CIRCUIT(intro_circ), RELAY_COMMAND_INTRODUCE2, (char*)request, request_len, NULL)) { log_warn(LD_GENERAL, "Unable to send INTRODUCE2 cell to Tor client."); goto err; } /* And sent an ack down Alice's circuit. Empty body means succeeded. */ if (relay_send_command_from_edge(0,TO_CIRCUIT(circ), RELAY_COMMAND_INTRODUCE_ACK, NULL,0,NULL)) { log_warn(LD_GENERAL, "Unable to send INTRODUCE_ACK cell to Tor client."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return -1; } return 0; err: /* Send the client an NACK */ nak_body[0] = 1; if (relay_send_command_from_edge(0,TO_CIRCUIT(circ), RELAY_COMMAND_INTRODUCE_ACK, nak_body, 1, NULL)) { log_warn(LD_GENERAL, "Unable to send NAK to Tor client."); /* Is this right? */ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); } return -1; }
/** Process a 'create' <b>cell</b> that just arrived from <b>conn</b>. Make a * new circuit with the p_circ_id specified in cell. Put the circuit in state * onionskin_pending, and pass the onionskin to the cpuworker. Circ will get * picked up again when the cpuworker finishes decrypting it. */ static void command_process_create_cell(cell_t *cell, or_connection_t *conn) { or_circuit_t *circ; const or_options_t *options = get_options(); int id_is_high; if (we_are_hibernating()) { log_info(LD_OR, "Received create cell but we're shutting down. Sending back " "destroy."); connection_or_send_destroy(cell->circ_id, conn, END_CIRC_REASON_HIBERNATING); return; } if (!server_mode(options) || (!public_server_mode(options) && conn->is_outgoing)) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received create cell (type %d) from %s:%d, but we're connected " "to it as a client. " "Sending back a destroy.", (int)cell->command, conn->_base.address, conn->_base.port); connection_or_send_destroy(cell->circ_id, conn, END_CIRC_REASON_TORPROTOCOL); return; } /* If the high bit of the circuit ID is not as expected, close the * circ. */ id_is_high = cell->circ_id & (1<<15); if ((id_is_high && conn->circ_id_type == CIRC_ID_TYPE_HIGHER) || (!id_is_high && conn->circ_id_type == CIRC_ID_TYPE_LOWER)) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received create cell with unexpected circ_id %d. Closing.", cell->circ_id); connection_or_send_destroy(cell->circ_id, conn, END_CIRC_REASON_TORPROTOCOL); return; } if (circuit_id_in_use_on_orconn(cell->circ_id, conn)) { const node_t *node = node_get_by_id(conn->identity_digest); log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received CREATE cell (circID %d) for known circ. " "Dropping (age %d).", cell->circ_id, (int)(time(NULL) - conn->_base.timestamp_created)); if (node) { char *p = esc_for_log(node_get_platform(node)); log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Details: router %s, platform %s.", node_describe(node), p); tor_free(p); } return; } circ = or_circuit_new(cell->circ_id, conn); circ->_base.purpose = CIRCUIT_PURPOSE_OR; circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_ONIONSKIN_PENDING); if (cell->command == CELL_CREATE) { char *onionskin = tor_malloc(ONIONSKIN_CHALLENGE_LEN); memcpy(onionskin, cell->payload, ONIONSKIN_CHALLENGE_LEN); /* hand it off to the cpuworkers, and then return. */ if (assign_onionskin_to_cpuworker(NULL, circ, onionskin) < 0) { #define WARN_HANDOFF_FAILURE_INTERVAL (6*60*60) static ratelim_t handoff_warning = RATELIM_INIT(WARN_HANDOFF_FAILURE_INTERVAL); char *m; if ((m = rate_limit_log(&handoff_warning, approx_time()))) { log_warn(LD_GENERAL,"Failed to hand off onionskin. Closing.%s",m); tor_free(m); } circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return; } log_debug(LD_OR,"success: handed off onionskin."); } else { /* This is a CREATE_FAST cell; we can handle it immediately without using * a CPU worker. */ char keys[CPATH_KEY_MATERIAL_LEN]; char reply[DIGEST_LEN*2]; tor_assert(cell->command == CELL_CREATE_FAST); /* Make sure we never try to use the OR connection on which we * received this cell to satisfy an EXTEND request, */ conn->is_connection_with_client = 1; if (fast_server_handshake(cell->payload, (uint8_t*)reply, (uint8_t*)keys, sizeof(keys))<0) { log_warn(LD_OR,"Failed to generate key material. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return; } if (onionskin_answer(circ, CELL_CREATED_FAST, reply, keys)<0) { log_warn(LD_OR,"Failed to reply to CREATE_FAST cell. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return; } } }
/** Respond to an ESTABLISH_INTRO cell by checking the signed data and * setting the circuit's purpose and service pk digest. */ int rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request, size_t request_len) { crypto_pk_t *pk = NULL; char buf[DIGEST_LEN+9]; char expected_digest[DIGEST_LEN]; char pk_digest[DIGEST_LEN]; size_t asn1len; or_circuit_t *c; char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; int reason = END_CIRC_REASON_INTERNAL; log_info(LD_REND, "Received an ESTABLISH_INTRO request on circuit %u", (unsigned) circ->p_circ_id); if (circ->base_.purpose != CIRCUIT_PURPOSE_OR || circ->base_.n_chan) { log_fn(LOG_PROTOCOL_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) goto truncated; /* First 2 bytes: length of asn1-encoded key. */ asn1len = ntohs(get_uint16(request)); /* Next asn1len bytes: asn1-encoded key. */ if (request_len < 2+DIGEST_LEN+asn1len) goto truncated; pk = crypto_pk_asn1_decode((char*)(request+2), asn1len); if (!pk) { reason = END_CIRC_REASON_TORPROTOCOL; log_warn(LD_PROTOCOL, "Couldn't decode public key."); goto err; } /* Next 20 bytes: Hash of rend_circ_nonce | "INTRODUCE" */ memcpy(buf, circ->rend_circ_nonce, DIGEST_LEN); memcpy(buf+DIGEST_LEN, "INTRODUCE", 9); if (crypto_digest(expected_digest, buf, DIGEST_LEN+9) < 0) { log_warn(LD_BUG, "Internal error computing digest."); goto err; } if (tor_memneq(expected_digest, request+2+asn1len, DIGEST_LEN)) { log_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 */ note_crypto_pk_op(REND_MID); if (crypto_pk_public_checksig_digest(pk, (char*)request, 2+asn1len+DIGEST_LEN, (char*)(request+2+DIGEST_LEN+asn1len), request_len-(2+DIGEST_LEN+asn1len))<0) { log_warn(LD_PROTOCOL, "Incorrect signature on ESTABLISH_INTRO cell; rejecting."); reason = END_CIRC_REASON_TORPROTOCOL; goto err; } /* The request is valid. First, compute the hash of Bob's PK.*/ if (crypto_pk_get_digest(pk, pk_digest)<0) { log_warn(LD_BUG, "Internal error: couldn't hash public key."); goto err; } crypto_pk_free(pk); /* don't need it anymore */ pk = NULL; /* so we don't free it again if err */ base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, pk_digest, REND_SERVICE_ID_LEN); /* Close any other intro circuits with the same pk. */ c = NULL; while ((c = circuit_get_intro_point((const uint8_t *)pk_digest))) { log_info(LD_REND, "Replacing old circuit for service %s", safe_str(serviceid)); circuit_mark_for_close(TO_CIRCUIT(c), END_CIRC_REASON_FINISHED); /* Now it's marked, and it won't be returned next time. */ } /* Acknowledge the request. */ if (relay_send_command_from_edge(0, TO_CIRCUIT(circ), RELAY_COMMAND_INTRO_ESTABLISHED, "", 0, NULL)<0) { log_info(LD_GENERAL, "Couldn't send INTRO_ESTABLISHED cell."); goto err; } /* Now, set up this circuit. */ circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT); circuit_set_intro_point_digest(circ, (uint8_t *)pk_digest); log_info(LD_REND, "Established introduction point on circuit %u for service %s", (unsigned) circ->p_circ_id, safe_str(serviceid)); return 0; truncated: log_warn(LD_PROTOCOL, "Rejecting truncated ESTABLISH_INTRO cell."); reason = END_CIRC_REASON_TORPROTOCOL; err: if (pk) crypto_pk_free(pk); circuit_mark_for_close(TO_CIRCUIT(circ), reason); return -1; }
/** Process a 'created' <b>cell</b> that just arrived from <b>chan</b>. * Find the circuit * that it's intended for. If we're not the origin of the circuit, package * the 'created' cell in an 'extended' relay cell and pass it back. If we * are the origin of the circuit, send it to circuit_finish_handshake() to * finish processing keys, and then call circuit_send_next_onion_skin() to * extend to the next hop in the circuit if necessary. */ static void command_process_created_cell(cell_t *cell, channel_t *chan) { circuit_t *circ; extended_cell_t extended_cell; circ = circuit_get_by_circid_channel(cell->circ_id, chan); if (!circ) { log_info(LD_OR, "(circID %u) unknown circ (probably got a destroy earlier). " "Dropping.", (unsigned)cell->circ_id); return; } if (circ->n_circ_id != cell->circ_id) { log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL, "got created cell from Tor client? Closing."); circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); return; } if (created_cell_parse(&extended_cell.created_cell, cell) < 0) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Unparseable created cell."); circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); return; } if (CIRCUIT_IS_ORIGIN(circ)) { /* we're the OP. Handshake this. */ origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ); int err_reason = 0; log_debug(LD_OR,"at OP. Finishing handshake."); if ((err_reason = circuit_finish_handshake(origin_circ, &extended_cell.created_cell)) < 0) { log_warn(LD_OR,"circuit_finish_handshake failed."); circuit_mark_for_close(circ, -err_reason); return; } log_debug(LD_OR,"Moving to next skin."); if ((err_reason = circuit_send_next_onion_skin(origin_circ)) < 0) { log_info(LD_OR,"circuit_send_next_onion_skin failed."); /* XXX push this circuit_close lower */ circuit_mark_for_close(circ, -err_reason); return; } } else { /* pack it into an extended relay cell, and send it. */ uint8_t command=0; uint16_t len=0; uint8_t payload[RELAY_PAYLOAD_SIZE]; log_debug(LD_OR, "Converting created cell to extended relay cell, sending."); memset(payload, 0, sizeof(payload)); if (extended_cell.created_cell.cell_type == CELL_CREATED2) extended_cell.cell_type = RELAY_COMMAND_EXTENDED2; else extended_cell.cell_type = RELAY_COMMAND_EXTENDED; if (extended_cell_format(&command, &len, payload, &extended_cell) < 0) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Can't format extended cell."); circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); return; } relay_send_command_from_edge(0, circ, command, (const char*)payload, len, NULL); } }
/** Process a 'relay' or 'relay_early' <b>cell</b> that just arrived from * <b>conn</b>. Make sure it came in with a recognized circ_id. Pass it on to * circuit_receive_relay_cell() for actual processing. */ static void command_process_relay_cell(cell_t *cell, channel_t *chan) { circuit_t *circ; int reason, direction; circ = circuit_get_by_circid_channel(cell->circ_id, chan); if (!circ) { log_debug(LD_OR, "unknown circuit %u on connection from %s. Dropping.", (unsigned)cell->circ_id, channel_get_canonical_remote_descr(chan)); return; } if (circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,"circuit in create_wait. Closing."); circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); return; } if (CIRCUIT_IS_ORIGIN(circ)) { /* if we're a relay and treating connections with recent local * traffic better, then this is one of them. */ channel_timestamp_client(chan); } if (!CIRCUIT_IS_ORIGIN(circ) && cell->circ_id == TO_OR_CIRCUIT(circ)->p_circ_id) direction = CELL_DIRECTION_OUT; else direction = CELL_DIRECTION_IN; /* If we have a relay_early cell, make sure that it's outbound, and we've * gotten no more than MAX_RELAY_EARLY_CELLS_PER_CIRCUIT of them. */ if (cell->command == CELL_RELAY_EARLY) { if (direction == CELL_DIRECTION_IN) { /* Allow an unlimited number of inbound relay_early cells, * for hidden service compatibility. There isn't any way to make * a long circuit through inbound relay_early cells anyway. See * bug 1038. -RD */ } else { or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); if (or_circ->remaining_relay_early_cells == 0) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Received too many RELAY_EARLY cells on circ %u from %s." " Closing circuit.", (unsigned)cell->circ_id, safe_str(channel_get_canonical_remote_descr(chan))); circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); return; } --or_circ->remaining_relay_early_cells; } } if ((reason = circuit_receive_relay_cell(cell, circ, direction)) < 0) { log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,"circuit_receive_relay_cell " "(%s) failed. Closing.", direction==CELL_DIRECTION_OUT?"forward":"backward"); circuit_mark_for_close(circ, -reason); } }