📄 relay.c
字号:
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2008, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* $Id$ */
const char relay_c_id[] =
"$Id$";
/**
* \file relay.c
* \brief Handle relay cell encryption/decryption, plus packaging and
* receiving from circuits, plus queueing on circuits.
**/
#include "or.h"
#include "mempool.h"
static int relay_crypt(circuit_t *circ, cell_t *cell, int cell_direction,
crypt_path_t **layer_hint, char *recognized);
static edge_connection_t *relay_lookup_conn(circuit_t *circ, cell_t *cell,
int cell_direction,
crypt_path_t *layer_hint);
static int
connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
edge_connection_t *conn,
crypt_path_t *layer_hint);
static void
circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint);
static void
circuit_resume_edge_reading(circuit_t *circ, crypt_path_t *layer_hint);
static int
circuit_resume_edge_reading_helper(edge_connection_t *conn,
circuit_t *circ,
crypt_path_t *layer_hint);
static int
circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint);
/** Stats: how many relay cells have originated at this hop, or have
* been relayed onward (not recognized at this hop)?
*/
uint64_t stats_n_relay_cells_relayed = 0;
/** Stats: how many relay cells have been delivered to streams at this
* hop?
*/
uint64_t stats_n_relay_cells_delivered = 0;
/** Update digest from the payload of cell. Assign integrity part to
* cell.
*/
static void
relay_set_digest(crypto_digest_env_t *digest, cell_t *cell)
{
char integrity[4];
relay_header_t rh;
crypto_digest_add_bytes(digest, cell->payload, CELL_PAYLOAD_SIZE);
crypto_digest_get_digest(digest, integrity, 4);
// log_fn(LOG_DEBUG,"Putting digest of %u %u %u %u into relay cell.",
// integrity[0], integrity[1], integrity[2], integrity[3]);
relay_header_unpack(&rh, cell->payload);
memcpy(rh.integrity, integrity, 4);
relay_header_pack(cell->payload, &rh);
}
/** Does the digest for this circuit indicate that this cell is for us?
*
* Update digest from the payload of cell (with the integrity part set
* to 0). If the integrity part is valid, return 1, else restore digest
* and cell to their original state and return 0.
*/
static int
relay_digest_matches(crypto_digest_env_t *digest, cell_t *cell)
{
char received_integrity[4], calculated_integrity[4];
relay_header_t rh;
crypto_digest_env_t *backup_digest=NULL;
backup_digest = crypto_digest_dup(digest);
relay_header_unpack(&rh, cell->payload);
memcpy(received_integrity, rh.integrity, 4);
memset(rh.integrity, 0, 4);
relay_header_pack(cell->payload, &rh);
// log_fn(LOG_DEBUG,"Reading digest of %u %u %u %u from relay cell.",
// received_integrity[0], received_integrity[1],
// received_integrity[2], received_integrity[3]);
crypto_digest_add_bytes(digest, cell->payload, CELL_PAYLOAD_SIZE);
crypto_digest_get_digest(digest, calculated_integrity, 4);
if (memcmp(received_integrity, calculated_integrity, 4)) {
// log_fn(LOG_INFO,"Recognized=0 but bad digest. Not recognizing.");
// (%d vs %d).", received_integrity, calculated_integrity);
/* restore digest to its old form */
crypto_digest_assign(digest, backup_digest);
/* restore the relay header */
memcpy(rh.integrity, received_integrity, 4);
relay_header_pack(cell->payload, &rh);
crypto_free_digest_env(backup_digest);
return 0;
}
crypto_free_digest_env(backup_digest);
return 1;
}
/** Apply <b>cipher</b> to CELL_PAYLOAD_SIZE bytes of <b>in</b>
* (in place).
*
* If <b>encrypt_mode</b> is 1 then encrypt, else decrypt.
*
* Return -1 if the crypto fails, else return 0.
*/
static int
relay_crypt_one_payload(crypto_cipher_env_t *cipher, char *in,
int encrypt_mode)
{
int r;
(void)encrypt_mode;
r = crypto_cipher_crypt_inplace(cipher, in, CELL_PAYLOAD_SIZE);
if (r) {
log_warn(LD_BUG,"Error during relay encryption");
return -1;
}
return 0;
}
/** Receive a relay cell:
* - Crypt it (encrypt if headed toward the origin or if we <b>are</b> the
* origin; decrypt if we're headed toward the exit).
* - Check if recognized (if exitward).
* - If recognized and the digest checks out, then find if there's a stream
* that the cell is intended for, and deliver it to the right
* connection_edge.
* - If not recognized, then we need to relay it: append it to the appropriate
* cell_queue on <b>circ</b>.
*
* Return -<b>reason</b> on failure.
*/
int
circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, int cell_direction)
{
or_connection_t *or_conn=NULL;
crypt_path_t *layer_hint=NULL;
char recognized=0;
int reason;
tor_assert(cell);
tor_assert(circ);
tor_assert(cell_direction == CELL_DIRECTION_OUT ||
cell_direction == CELL_DIRECTION_IN);
if (circ->marked_for_close)
return 0;
if (relay_crypt(circ, cell, cell_direction, &layer_hint, &recognized) < 0) {
log_warn(LD_BUG,"relay crypt failed. Dropping connection.");
return -END_CIRC_REASON_INTERNAL;
}
if (recognized) {
edge_connection_t *conn = relay_lookup_conn(circ, cell, cell_direction,
layer_hint);
if (cell_direction == CELL_DIRECTION_OUT) {
++stats_n_relay_cells_delivered;
log_debug(LD_OR,"Sending away from origin.");
if ((reason=connection_edge_process_relay_cell(cell, circ, conn, NULL))
< 0) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"connection_edge_process_relay_cell (away from origin) "
"failed.");
return reason;
}
}
if (cell_direction == CELL_DIRECTION_IN) {
++stats_n_relay_cells_delivered;
log_debug(LD_OR,"Sending to origin.");
if ((reason = connection_edge_process_relay_cell(cell, circ, conn,
layer_hint)) < 0) {
log_warn(LD_OR,
"connection_edge_process_relay_cell (at origin) failed.");
return reason;
}
}
return 0;
}
/* not recognized. pass it on. */
if (cell_direction == CELL_DIRECTION_OUT) {
cell->circ_id = circ->n_circ_id; /* switch it */
or_conn = circ->n_conn;
} else if (! CIRCUIT_IS_ORIGIN(circ)) {
cell->circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; /* switch it */
or_conn = TO_OR_CIRCUIT(circ)->p_conn;
} else {
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"Dropping unrecognized inbound cell on origin circuit.");
return 0;
}
if (!or_conn) {
// XXXX Can this splice stuff be done more cleanly?
if (! CIRCUIT_IS_ORIGIN(circ) &&
TO_OR_CIRCUIT(circ)->rend_splice &&
cell_direction == CELL_DIRECTION_OUT) {
or_circuit_t *splice = TO_OR_CIRCUIT(circ)->rend_splice;
tor_assert(circ->purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED);
tor_assert(splice->_base.purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED);
cell->circ_id = splice->p_circ_id;
if ((reason = circuit_receive_relay_cell(cell, TO_CIRCUIT(splice),
CELL_DIRECTION_IN)) < 0) {
log_warn(LD_REND, "Error relaying cell across rendezvous; closing "
"circuits");
/* XXXX Do this here, or just return -1? */
circuit_mark_for_close(circ, -reason);
return reason;
}
return 0;
}
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Didn't recognize cell, but circ stops here! Closing circ.");
return -END_CIRC_REASON_TORPROTOCOL;
}
log_debug(LD_OR,"Passing on unrecognized cell.");
++stats_n_relay_cells_relayed; /* XXXX no longer quite accurate {cells}
* we might kill the circ before we relay
* the cells. */
append_cell_to_circuit_queue(circ, or_conn, cell, cell_direction);
return 0;
}
/** Do the appropriate en/decryptions for <b>cell</b> arriving on
* <b>circ</b> in direction <b>cell_direction</b>.
*
* If cell_direction == CELL_DIRECTION_IN:
* - If we're at the origin (we're the OP), for hops 1..N,
* decrypt cell. If recognized, stop.
* - Else (we're not the OP), encrypt one hop. Cell is not recognized.
*
* If cell_direction == CELL_DIRECTION_OUT:
* - decrypt one hop. Check if recognized.
*
* If cell is recognized, set *recognized to 1, and set
* *layer_hint to the hop that recognized it.
*
* Return -1 to indicate that we should mark the circuit for close,
* else return 0.
*/
static int
relay_crypt(circuit_t *circ, cell_t *cell, int cell_direction,
crypt_path_t **layer_hint, char *recognized)
{
relay_header_t rh;
tor_assert(circ);
tor_assert(cell);
tor_assert(recognized);
tor_assert(cell_direction == CELL_DIRECTION_IN ||
cell_direction == CELL_DIRECTION_OUT);
if (cell_direction == CELL_DIRECTION_IN) {
if (CIRCUIT_IS_ORIGIN(circ)) { /* We're at the beginning of the circuit.
* We'll want to do layered decrypts. */
crypt_path_t *thishop, *cpath = TO_ORIGIN_CIRCUIT(circ)->cpath;
thishop = cpath;
if (thishop->state != CPATH_STATE_OPEN) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Relay cell before first created cell? Closing.");
return -1;
}
do { /* Remember: cpath is in forward order, that is, first hop first. */
tor_assert(thishop);
if (relay_crypt_one_payload(thishop->b_crypto, cell->payload, 0) < 0)
return -1;
relay_header_unpack(&rh, cell->payload);
if (rh.recognized == 0) {
/* it's possibly recognized. have to check digest to be sure. */
if (relay_digest_matches(thishop->b_digest, cell)) {
*recognized = 1;
*layer_hint = thishop;
return 0;
}
}
thishop = thishop->next;
} while (thishop != cpath && thishop->state == CPATH_STATE_OPEN);
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"Incoming cell at client not recognized. Closing.");
return -1;
} else { /* we're in the middle. Just one crypt. */
if (relay_crypt_one_payload(TO_OR_CIRCUIT(circ)->p_crypto,
cell->payload, 1) < 0)
return -1;
// log_fn(LOG_DEBUG,"Skipping recognized check, because we're not "
// "the client.");
}
} else /* cell_direction == CELL_DIRECTION_OUT */ {
/* we're in the middle. Just one crypt. */
if (relay_crypt_one_payload(TO_OR_CIRCUIT(circ)->n_crypto,
cell->payload, 0) < 0)
return -1;
relay_header_unpack(&rh, cell->payload);
if (rh.recognized == 0) {
/* it's possibly recognized. have to check digest to be sure. */
if (relay_digest_matches(TO_OR_CIRCUIT(circ)->n_digest, cell)) {
*recognized = 1;
return 0;
}
}
}
return 0;
}
/** Package a relay cell from an edge:
* - Encrypt it to the right layer
* - Append it to the appropriate cell_queue on <b>circ</b>.
*/
static int
circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
int cell_direction,
crypt_path_t *layer_hint)
{
or_connection_t *conn; /* where to send the cell */
if (cell_direction == CELL_DIRECTION_OUT) {
crypt_path_t *thishop; /* counter for repeated crypts */
conn = circ->n_conn;
if (!CIRCUIT_IS_ORIGIN(circ) || !conn) {
log_warn(LD_BUG,"outgoing relay cell has n_conn==NULL. Dropping.");
return 0; /* just drop it */
}
relay_set_digest(layer_hint->f_digest, cell);
thishop = layer_hint;
/* moving from farthest to nearest hop */
do {
tor_assert(thishop);
/* XXXX RD This is a bug, right? */
log_debug(LD_OR,"crypting a layer of the relay cell.");
if (relay_crypt_one_payload(thishop->f_crypto, cell->payload, 1) < 0) {
return -1;
}
thishop = thishop->prev;
} while (thishop != TO_ORIGIN_CIRCUIT(circ)->cpath->prev);
} else { /* incoming cell */
or_circuit_t *or_circ;
if (CIRCUIT_IS_ORIGIN(circ)) {
/* We should never package an _incoming_ cell from the circuit
* origin; that means we messed up somewhere. */
log_warn(LD_BUG,"incoming relay cell at origin circuit. Dropping.");
assert_circuit_ok(circ);
return 0; /* just drop it */
}
or_circ = TO_OR_CIRCUIT(circ);
conn = or_circ->p_conn;
relay_set_digest(or_circ->p_digest, cell);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -