📄 relay.c
字号:
if (relay_crypt_one_payload(or_circ->p_crypto, cell->payload, 1) < 0)
return -1;
}
++stats_n_relay_cells_relayed;
append_cell_to_circuit_queue(circ, conn, cell, cell_direction);
return 0;
}
/** If cell's stream_id matches the stream_id of any conn that's
* attached to circ, return that conn, else return NULL.
*/
static edge_connection_t *
relay_lookup_conn(circuit_t *circ, cell_t *cell, int cell_direction,
crypt_path_t *layer_hint)
{
edge_connection_t *tmpconn;
relay_header_t rh;
relay_header_unpack(&rh, cell->payload);
if (!rh.stream_id)
return NULL;
/* IN or OUT cells could have come from either direction, now
* that we allow rendezvous *to* an OP.
*/
if (CIRCUIT_IS_ORIGIN(circ)) {
for (tmpconn = TO_ORIGIN_CIRCUIT(circ)->p_streams; tmpconn;
tmpconn=tmpconn->next_stream) {
if (rh.stream_id == tmpconn->stream_id &&
!tmpconn->_base.marked_for_close &&
tmpconn->cpath_layer == layer_hint) {
log_debug(LD_APP,"found conn for stream %d.", rh.stream_id);
return tmpconn;
}
}
} else {
for (tmpconn = TO_OR_CIRCUIT(circ)->n_streams; tmpconn;
tmpconn=tmpconn->next_stream) {
if (rh.stream_id == tmpconn->stream_id &&
!tmpconn->_base.marked_for_close) {
log_debug(LD_EXIT,"found conn for stream %d.", rh.stream_id);
if (cell_direction == CELL_DIRECTION_OUT ||
connection_edge_is_rendezvous_stream(tmpconn))
return tmpconn;
}
}
for (tmpconn = TO_OR_CIRCUIT(circ)->resolving_streams; tmpconn;
tmpconn=tmpconn->next_stream) {
if (rh.stream_id == tmpconn->stream_id &&
!tmpconn->_base.marked_for_close) {
log_debug(LD_EXIT,"found conn for stream %d.", rh.stream_id);
return tmpconn;
}
}
}
return NULL; /* probably a begin relay cell */
}
/** Pack the relay_header_t host-order structure <b>src</b> into
* network-order in the buffer <b>dest</b>. See tor-spec.txt for details
* about the wire format.
*/
void
relay_header_pack(char *dest, const relay_header_t *src)
{
*(uint8_t*)(dest) = src->command;
set_uint16(dest+1, htons(src->recognized));
set_uint16(dest+3, htons(src->stream_id));
memcpy(dest+5, src->integrity, 4);
set_uint16(dest+9, htons(src->length));
}
/** Unpack the network-order buffer <b>src</b> into a host-order
* relay_header_t structure <b>dest</b>.
*/
void
relay_header_unpack(relay_header_t *dest, const char *src)
{
dest->command = *(uint8_t*)(src);
dest->recognized = ntohs(get_uint16(src+1));
dest->stream_id = ntohs(get_uint16(src+3));
memcpy(dest->integrity, src+5, 4);
dest->length = ntohs(get_uint16(src+9));
}
/** Make a relay cell out of <b>relay_command</b> and <b>payload</b>, and send
* it onto the open circuit <b>circ</b>. <b>stream_id</b> is the ID on
* <b>circ</b> for the stream that's sending the relay cell, or 0 if it's a
* control cell. <b>cpath_layer</b> is NULL for OR->OP cells, or the
* destination hop for OP->OR cells.
*
* If you can't send the cell, mark the circuit for close and return -1. Else
* return 0.
*/
int
relay_send_command_from_edge(uint16_t stream_id, circuit_t *circ,
uint8_t relay_command, const char *payload,
size_t payload_len, crypt_path_t *cpath_layer)
{
cell_t cell;
relay_header_t rh;
int cell_direction;
/* XXXX NM Split this function into a separate versions per circuit type? */
tor_assert(circ);
tor_assert(payload_len <= RELAY_PAYLOAD_SIZE);
memset(&cell, 0, sizeof(cell_t));
cell.command = CELL_RELAY;
if (cpath_layer) {
cell.circ_id = circ->n_circ_id;
cell_direction = CELL_DIRECTION_OUT;
} else if (! CIRCUIT_IS_ORIGIN(circ)) {
cell.circ_id = TO_OR_CIRCUIT(circ)->p_circ_id;
cell_direction = CELL_DIRECTION_IN;
} else {
return -1;
}
memset(&rh, 0, sizeof(rh));
rh.command = relay_command;
rh.stream_id = stream_id;
rh.length = payload_len;
relay_header_pack(cell.payload, &rh);
if (payload_len)
memcpy(cell.payload+RELAY_HEADER_SIZE, payload, payload_len);
log_debug(LD_OR,"delivering %d cell %s.", relay_command,
cell_direction == CELL_DIRECTION_OUT ? "forward" : "backward");
if (cell_direction == CELL_DIRECTION_OUT && circ->n_conn) {
/* if we're using relaybandwidthrate, this conn wants priority */
/* XXXX021 the call to time() seems little too frequent */
circ->n_conn->client_used = time(NULL);
}
if (circuit_package_relay_cell(&cell, circ, cell_direction, cpath_layer)
< 0) {
log_warn(LD_BUG,"circuit_package_relay_cell failed. Closing.");
circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
return -1;
}
return 0;
}
/** Make a relay cell out of <b>relay_command</b> and <b>payload</b>, and
* send it onto the open circuit <b>circ</b>. <b>fromconn</b> is the stream
* that's sending the relay cell, or NULL if it's a control cell.
* <b>cpath_layer</b> is NULL for OR->OP cells, or the destination hop
* for OP->OR cells.
*
* If you can't send the cell, mark the circuit for close and
* return -1. Else return 0.
*/
int
connection_edge_send_command(edge_connection_t *fromconn,
uint8_t relay_command, const char *payload,
size_t payload_len)
{
/* XXXX NM Split this function into a separate versions per circuit type? */
circuit_t *circ;
tor_assert(fromconn);
circ = fromconn->on_circuit;
if (fromconn->_base.marked_for_close) {
log_warn(LD_BUG,
"called on conn that's already marked for close at %s:%d.",
fromconn->_base.marked_for_close_file,
fromconn->_base.marked_for_close);
return 0;
}
if (!circ) {
if (fromconn->_base.type == CONN_TYPE_AP) {
log_info(LD_APP,"no circ. Closing conn.");
connection_mark_unattached_ap(fromconn, END_STREAM_REASON_INTERNAL);
} else {
log_info(LD_EXIT,"no circ. Closing conn.");
fromconn->_base.edge_has_sent_end = 1; /* no circ to send to */
fromconn->end_reason = END_STREAM_REASON_INTERNAL;
connection_mark_for_close(TO_CONN(fromconn));
}
return -1;
}
return relay_send_command_from_edge(fromconn->stream_id, circ,
relay_command, payload,
payload_len, fromconn->cpath_layer);
}
/** Translate <b>reason</b>, which came from a relay 'end' cell,
* into a static const string describing why the stream is closing.
* <b>reason</b> is -1 if no reason was provided.
*/
static const char *
connection_edge_end_reason_str(int reason)
{
switch (reason) {
case -1:
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"End cell arrived with length 0. Should be at least 1.");
return "MALFORMED";
case END_STREAM_REASON_MISC: return "misc error";
case END_STREAM_REASON_RESOLVEFAILED: return "resolve failed";
case END_STREAM_REASON_CONNECTREFUSED: return "connection refused";
case END_STREAM_REASON_EXITPOLICY: return "exit policy failed";
case END_STREAM_REASON_DESTROY: return "destroyed";
case END_STREAM_REASON_DONE: return "closed normally";
case END_STREAM_REASON_TIMEOUT: return "gave up (timeout)";
case END_STREAM_REASON_HIBERNATING: return "server is hibernating";
case END_STREAM_REASON_INTERNAL: return "internal error at server";
case END_STREAM_REASON_RESOURCELIMIT: return "server out of resources";
case END_STREAM_REASON_CONNRESET: return "connection reset";
case END_STREAM_REASON_TORPROTOCOL: return "Tor protocol error";
case END_STREAM_REASON_NOTDIRECTORY: return "not a directory";
default:
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Reason for ending (%d) not recognized.",reason);
return "unknown";
}
}
/** Translate <b>reason</b> (as from a relay 'end' cell) into an
* appropriate SOCKS5 reply code.
*
* A reason of 0 means that we're not actually expecting to send
* this code back to the socks client; we just call it 'succeeded'
* to keep things simple.
*/
socks5_reply_status_t
connection_edge_end_reason_socks5_response(int reason)
{
switch (reason & END_STREAM_REASON_MASK) {
case 0:
return SOCKS5_SUCCEEDED;
case END_STREAM_REASON_MISC:
return SOCKS5_GENERAL_ERROR;
case END_STREAM_REASON_RESOLVEFAILED:
return SOCKS5_HOST_UNREACHABLE;
case END_STREAM_REASON_CONNECTREFUSED:
return SOCKS5_CONNECTION_REFUSED;
case END_STREAM_REASON_ENTRYPOLICY:
return SOCKS5_NOT_ALLOWED;
case END_STREAM_REASON_EXITPOLICY:
return SOCKS5_NOT_ALLOWED;
case END_STREAM_REASON_DESTROY:
return SOCKS5_GENERAL_ERROR;
case END_STREAM_REASON_DONE:
return SOCKS5_SUCCEEDED;
case END_STREAM_REASON_TIMEOUT:
return SOCKS5_TTL_EXPIRED;
case END_STREAM_REASON_RESOURCELIMIT:
return SOCKS5_GENERAL_ERROR;
case END_STREAM_REASON_HIBERNATING:
return SOCKS5_GENERAL_ERROR;
case END_STREAM_REASON_INTERNAL:
return SOCKS5_GENERAL_ERROR;
case END_STREAM_REASON_CONNRESET:
return SOCKS5_CONNECTION_REFUSED;
case END_STREAM_REASON_TORPROTOCOL:
return SOCKS5_GENERAL_ERROR;
case END_STREAM_REASON_CANT_ATTACH:
return SOCKS5_GENERAL_ERROR;
case END_STREAM_REASON_NET_UNREACHABLE:
return SOCKS5_NET_UNREACHABLE;
case END_STREAM_REASON_SOCKSPROTOCOL:
return SOCKS5_GENERAL_ERROR;
default:
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Reason for ending (%d) not recognized; "
"sending generic socks error.", reason);
return SOCKS5_GENERAL_ERROR;
}
}
/* We need to use a few macros to deal with the fact that Windows
* decided that their sockets interface should be a permakludge.
* E_CASE is for errors where windows has both a EFOO and a WSAEFOO
* version, and S_CASE is for errors where windows has only a WSAEFOO
* version. (The E is for 'error', the S is for 'socket'). */
#ifdef MS_WINDOWS
#define E_CASE(s) case s: case WSA ## s
#define S_CASE(s) case WSA ## s
#else
#define E_CASE(s) case s
#define S_CASE(s) case s
#endif
/** Given an errno from a failed exit connection, return a reason code
* appropriate for use in a RELAY END cell.
*/
int
errno_to_end_reason(int e)
{
switch (e) {
case EPIPE:
return END_STREAM_REASON_DONE;
E_CASE(EBADF):
E_CASE(EFAULT):
E_CASE(EINVAL):
S_CASE(EISCONN):
S_CASE(ENOTSOCK):
S_CASE(EPROTONOSUPPORT):
S_CASE(EAFNOSUPPORT):
E_CASE(EACCES):
S_CASE(ENOTCONN):
S_CASE(ENETUNREACH):
return END_STREAM_REASON_INTERNAL;
S_CASE(ECONNREFUSED):
return END_STREAM_REASON_CONNECTREFUSED;
S_CASE(ECONNRESET):
return END_STREAM_REASON_CONNRESET;
S_CASE(ETIMEDOUT):
return END_STREAM_REASON_TIMEOUT;
S_CASE(ENOBUFS):
case ENOMEM:
case ENFILE:
E_CASE(EMFILE):
return END_STREAM_REASON_RESOURCELIMIT;
default:
log_info(LD_EXIT, "Didn't recognize errno %d (%s); telling the client "
"that we are ending a stream for 'misc' reason.",
e, tor_socket_strerror(e));
return END_STREAM_REASON_MISC;
}
}
/** How many times will I retry a stream that fails due to DNS
* resolve failure or misc error?
*/
#define MAX_RESOLVE_FAILURES 3
/** Return 1 if reason is something that you should retry if you
* get the end cell before you've connected; else return 0. */
static int
edge_reason_is_retriable(int reason)
{
return reason == END_STREAM_REASON_HIBERNATING ||
reason == END_STREAM_REASON_RESOURCELIMIT ||
reason == END_STREAM_REASON_EXITPOLICY ||
reason == END_STREAM_REASON_RESOLVEFAILED ||
reason == END_STREAM_REASON_MISC;
}
/** Called when we receive an END cell on a stream that isn't open yet.
* Arguments are as for connection_edge_process_relay_cell().
*/
static int
connection_edge_process_end_not_open(
relay_header_t *rh, cell_t *cell, origin_circuit_t *circ,
edge_connection_t *conn, crypt_path_t *layer_hint)
{
struct in_addr in;
routerinfo_t *exitrouter;
int reason = *(cell->payload+RELAY_HEADER_SIZE);
int control_reason = reason | END_STREAM_REASON_FLAG_REMOTE;
(void) layer_hint; /* unused */
if (rh->length > 0 && edge_reason_is_retriable(reason) &&
conn->_base.type == CONN_TYPE_AP) {
log_info(LD_APP,"Address '%s' refused due to '%s'. Considering retrying.",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -