/** 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); } } }
/** 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); } }
/** 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); } }
/** 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; }