📄 relay.c
字号:
safe_str(conn->socks_request->address),
connection_edge_end_reason_str(reason));
exitrouter =
router_get_by_digest(circ->build_state->chosen_exit->identity_digest);
switch (reason) {
case END_STREAM_REASON_EXITPOLICY:
if (rh->length >= 5) {
uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1));
int ttl;
if (!addr) {
log_info(LD_APP,"Address '%s' resolved to 0.0.0.0. Closing,",
safe_str(conn->socks_request->address));
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return 0;
}
if (rh->length >= 9)
ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+5));
else
ttl = -1;
client_dns_set_addressmap(conn->socks_request->address, addr,
conn->chosen_exit_name, ttl);
}
/* check if he *ought* to have allowed it */
if (exitrouter &&
(rh->length < 5 ||
(tor_inet_aton(conn->socks_request->address, &in) &&
!conn->chosen_exit_name))) {
log_info(LD_APP,
"Exitrouter '%s' seems to be more restrictive than its exit "
"policy. Not using this router as exit for now.",
exitrouter->nickname);
policies_set_router_exitpolicy_to_reject_all(exitrouter);
}
/* rewrite it to an IP if we learned one. */
if (addressmap_rewrite(conn->socks_request->address,
sizeof(conn->socks_request->address),
NULL)) {
control_event_stream_status(conn, STREAM_EVENT_REMAP, 0);
}
if (conn->_base.chosen_exit_optional ||
conn->_base.chosen_exit_retries) {
/* stop wanting a specific exit */
conn->_base.chosen_exit_optional = 0;
/* A non-zero chosen_exit_retries can happen if we set a
* TrackHostExits for this address under a port that the exit
* relay allows, but then try the same address with a different
* port that it doesn't allow to exit. We shouldn't unregister
* the mapping, since it is probably still wanted on the
* original port. But now we give away to the exit relay that
* we probably have a TrackHostExits on it. So be it. */
conn->_base.chosen_exit_retries = 0;
tor_free(conn->chosen_exit_name); /* clears it */
}
if (connection_ap_detach_retriable(conn, circ, control_reason) >= 0)
return 0;
/* else, conn will get closed below */
break;
case END_STREAM_REASON_CONNECTREFUSED:
if (!conn->_base.chosen_exit_optional)
break; /* break means it'll close, below */
/* Else fall through: expire this circuit, clear the
* chosen_exit_name field, and try again. */
case END_STREAM_REASON_RESOLVEFAILED:
case END_STREAM_REASON_TIMEOUT:
case END_STREAM_REASON_MISC:
if (client_dns_incr_failures(conn->socks_request->address)
< MAX_RESOLVE_FAILURES) {
/* We haven't retried too many times; reattach the connection. */
circuit_log_path(LOG_INFO,LD_APP,circ);
tor_assert(circ->_base.timestamp_dirty);
circ->_base.timestamp_dirty -= get_options()->MaxCircuitDirtiness;
if (conn->_base.chosen_exit_optional) {
/* stop wanting a specific exit */
conn->_base.chosen_exit_optional = 0;
tor_free(conn->chosen_exit_name); /* clears it */
}
if (connection_ap_detach_retriable(conn, circ, control_reason) >= 0)
return 0;
/* else, conn will get closed below */
} else {
log_notice(LD_APP,
"Have tried resolving or connecting to address '%s' "
"at %d different places. Giving up.",
safe_str(conn->socks_request->address),
MAX_RESOLVE_FAILURES);
/* clear the failures, so it will have a full try next time */
client_dns_clear_failures(conn->socks_request->address);
}
break;
case END_STREAM_REASON_HIBERNATING:
case END_STREAM_REASON_RESOURCELIMIT:
if (exitrouter) {
policies_set_router_exitpolicy_to_reject_all(exitrouter);
}
if (conn->_base.chosen_exit_optional) {
/* stop wanting a specific exit */
conn->_base.chosen_exit_optional = 0;
tor_free(conn->chosen_exit_name); /* clears it */
}
if (connection_ap_detach_retriable(conn, circ, control_reason) >= 0)
return 0;
/* else, will close below */
break;
} /* end switch */
log_info(LD_APP,"Giving up on retrying; conn can't be handled.");
}
log_info(LD_APP,
"Edge got end (%s) before we're connected. Marking for close.",
connection_edge_end_reason_str(rh->length > 0 ? reason : -1));
if (conn->_base.type == CONN_TYPE_AP) {
circuit_log_path(LOG_INFO,LD_APP,circ);
/* need to test because of detach_retriable*/
if (!conn->_base.marked_for_close)
connection_mark_unattached_ap(conn, control_reason);
} else {
/* we just got an 'end', don't need to send one */
conn->_base.edge_has_sent_end = 1;
conn->end_reason = control_reason;
connection_mark_for_close(TO_CONN(conn));
}
return 0;
}
/** Helper: change the socks_request->address field on conn to the
* dotted-quad representation of <b>new_addr</b> (given in host order),
* and send an appropriate REMAP event. */
static void
remap_event_helper(edge_connection_t *conn, uint32_t new_addr)
{
struct in_addr in;
in.s_addr = htonl(new_addr);
tor_inet_ntoa(&in, conn->socks_request->address,
sizeof(conn->socks_request->address));
control_event_stream_status(conn, STREAM_EVENT_REMAP,
REMAP_STREAM_SOURCE_EXIT);
}
/** An incoming relay cell has arrived from circuit <b>circ</b> to
* stream <b>conn</b>.
*
* The arguments here are the same as in
* connection_edge_process_relay_cell() below; this function is called
* from there when <b>conn</b> is defined and not in an open state.
*/
static int
connection_edge_process_relay_cell_not_open(
relay_header_t *rh, cell_t *cell, circuit_t *circ,
edge_connection_t *conn, crypt_path_t *layer_hint)
{
if (rh->command == RELAY_COMMAND_END) {
if (CIRCUIT_IS_ORIGIN(circ))
return connection_edge_process_end_not_open(rh, cell,
TO_ORIGIN_CIRCUIT(circ), conn,
layer_hint);
else
return 0;
}
if (conn->_base.type == CONN_TYPE_AP &&
rh->command == RELAY_COMMAND_CONNECTED) {
tor_assert(CIRCUIT_IS_ORIGIN(circ));
if (conn->_base.state != AP_CONN_STATE_CONNECT_WAIT) {
log_fn(LOG_PROTOCOL_WARN, LD_APP,
"Got 'connected' while not in state connect_wait. Dropping.");
return 0;
}
conn->_base.state = AP_CONN_STATE_OPEN;
log_info(LD_APP,"'connected' received after %d seconds.",
(int)(time(NULL) - conn->_base.timestamp_lastread));
if (rh->length >= 4) {
uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE));
int ttl;
if (!addr || (get_options()->ClientDNSRejectInternalAddresses &&
is_internal_IP(addr, 0))) {
char buf[INET_NTOA_BUF_LEN];
struct in_addr a;
a.s_addr = htonl(addr);
tor_inet_ntoa(&a, buf, sizeof(buf));
log_info(LD_APP,
"...but it claims the IP address was %s. Closing.", buf);
connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return 0;
}
if (rh->length >= 8)
ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+4));
else
ttl = -1;
client_dns_set_addressmap(conn->socks_request->address, addr,
conn->chosen_exit_name, ttl);
remap_event_helper(conn, addr);
}
circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ));
/* don't send a socks reply to transparent conns */
if (!conn->socks_request->has_finished)
connection_ap_handshake_socks_reply(conn, NULL, 0, 0);
/* handle anything that might have queued */
if (connection_edge_package_raw_inbuf(conn, 1) < 0) {
/* (We already sent an end cell if possible) */
connection_mark_for_close(TO_CONN(conn));
return 0;
}
return 0;
}
if (conn->_base.type == CONN_TYPE_AP &&
rh->command == RELAY_COMMAND_RESOLVED) {
int ttl;
int answer_len;
uint8_t answer_type;
if (conn->_base.state != AP_CONN_STATE_RESOLVE_WAIT) {
log_fn(LOG_PROTOCOL_WARN, LD_APP, "Got a 'resolved' cell while "
"not in state resolve_wait. Dropping.");
return 0;
}
tor_assert(SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command));
answer_len = cell->payload[RELAY_HEADER_SIZE+1];
if (rh->length < 2 || answer_len+2>rh->length) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Dropping malformed 'resolved' cell");
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return 0;
}
answer_type = cell->payload[RELAY_HEADER_SIZE];
if (rh->length >= answer_len+6)
ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+
2+answer_len));
else
ttl = -1;
if (answer_type == RESOLVED_TYPE_IPV4 && answer_len >= 4) {
uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+2));
if (get_options()->ClientDNSRejectInternalAddresses &&
is_internal_IP(addr, 0)) {
char buf[INET_NTOA_BUF_LEN];
struct in_addr a;
a.s_addr = htonl(addr);
tor_inet_ntoa(&a, buf, sizeof(buf));
log_info(LD_APP,"Got a resolve with answer %s. Rejecting.", buf);
connection_ap_handshake_socks_resolved(conn,
RESOLVED_TYPE_ERROR_TRANSIENT,
0, NULL, 0, TIME_MAX);
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return 0;
}
}
connection_ap_handshake_socks_resolved(conn,
answer_type,
cell->payload[RELAY_HEADER_SIZE+1], /*answer_len*/
cell->payload+RELAY_HEADER_SIZE+2, /*answer*/
ttl,
-1);
if (answer_type == RESOLVED_TYPE_IPV4) {
uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+2));
remap_event_helper(conn, addr);
}
connection_mark_unattached_ap(conn,
END_STREAM_REASON_DONE |
END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
return 0;
}
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Got an unexpected relay command %d, in state %d (%s). Dropping.",
rh->command, conn->_base.state,
conn_state_to_string(conn->_base.type, conn->_base.state));
return 0; /* for forward compatibility, don't kill the circuit */
// connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
// connection_mark_for_close(conn);
// return -1;
}
/** An incoming relay cell has arrived on circuit <b>circ</b>. If
* <b>conn</b> is NULL this is a control cell, else <b>cell</b> is
* destined for <b>conn</b>.
*
* If <b>layer_hint</b> is defined, then we're the origin of the
* circuit, and it specifies the hop that packaged <b>cell</b>.
*
* Return -reason if you want to warn and tear down the circuit, else 0.
*/
static int
connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
edge_connection_t *conn,
crypt_path_t *layer_hint)
{
static int num_seen=0;
relay_header_t rh;
unsigned domain = layer_hint?LD_APP:LD_EXIT;
int reason;
tor_assert(cell);
tor_assert(circ);
relay_header_unpack(&rh, cell->payload);
// log_fn(LOG_DEBUG,"command %d stream %d", rh.command, rh.stream_id);
num_seen++;
log_debug(domain, "Now seen %d relay cells here.", num_seen);
if (rh.length > RELAY_PAYLOAD_SIZE) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Relay cell length field too long. Closing circuit.");
return - END_CIRC_REASON_TORPROTOCOL;
}
/* either conn is NULL, in which case we've got a control cell, or else
* conn points to the recognized stream. */
if (conn && !connection_state_is_open(TO_CONN(conn)))
return connection_edge_process_relay_cell_not_open(
&rh, cell, circ, conn, layer_hint);
switch (rh.command) {
case RELAY_COMMAND_DROP:
// log_info(domain,"Got a relay-level padding cell. Dropping.");
return 0;
case RELAY_COMMAND_BEGIN:
case RELAY_COMMAND_BEGIN_DIR:
if (layer_hint &&
circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) {
log_fn(LOG_PROTOCOL_WARN, LD_APP,
"Relay begin request unsupported at AP. Dropping.");
return 0;
}
if (conn) {
log_fn(LOG_PROTOCOL_WARN, domain,
"Begin cell for known stream. Dropping.");
return 0;
}
return connection_exit_begin_conn(cell, circ);
case RELAY_COMMAND_DATA:
++stats_n_data_cells_received;
if (( layer_hint && --layer_hint->deliver_window < 0) ||
(!layer_hint && --circ->deliver_window < 0)) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"(relay data) circ deliver_window below 0. Killing.");
connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
connection_mark_for_close(TO_CONN(conn));
return -END_CIRC_REASON_TORPROTOCOL;
}
log_debug(domain,"circ deliver_window now %d.", layer_hint ?
layer_hint->deliver_window : circ->deliver_window);
circuit_consider_sending_sendme(circ, layer_hint);
if (!conn) {
log_info(domain,"data cell dropped, unknown stream.");
return 0;
}
if (--conn->deliver_window < 0) { /* is it below 0 after decrement? */
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"(relay data) conn deliver_window below 0. Killing.");
return -END_CIRC_REASON_TORPROTOCOL;
}
stats_n_data_bytes_received += rh.length;
connection_write_to_buf(cell->payload + RELAY_HEADER_SIZE,
rh.length, TO_CONN(conn));
connection_edge_consider_sending_sendme(conn);
return 0;
case RELAY_COMMAND_END:
reason = rh.length > 0 ?
*(uint8_t *)(cell->payload+RELAY_HEADER_SIZE) : END_STREAM_REASON_MISC;
if (!conn) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -