📄 connection_edge.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 connection_edge_c_id[] =
"$Id$";
/**
* \file connection_edge.c
* \brief Handle edge streams.
**/
#include "or.h"
#ifdef HAVE_LINUX_NETFILTER_IPV4_H
#include <linux/netfilter_ipv4.h>
#define TRANS_NETFILTER
#endif
#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H)
#include <net/if.h>
#include <net/pfvar.h>
#define TRANS_PF
#endif
/** List of exit_redirect_t for every configured RedirectExit. */
static smartlist_t *redirect_exit_list = NULL;
static int connection_ap_handshake_process_socks(edge_connection_t *conn);
static int connection_ap_process_natd(edge_connection_t *conn);
static int connection_exit_connect_dir(edge_connection_t *exitconn);
static int address_is_in_virtual_range(const char *addr);
static int consider_plaintext_ports(edge_connection_t *conn, uint16_t port);
static void clear_trackexithost_mappings(const char *exitname);
/** An AP stream has failed/finished. If it hasn't already sent back
* a socks reply, send one now (based on endreason). Also set
* has_sent_end to 1, and mark the conn.
*/
void
_connection_mark_unattached_ap(edge_connection_t *conn, int endreason,
int line, const char *file)
{
tor_assert(conn->_base.type == CONN_TYPE_AP);
conn->_base.edge_has_sent_end = 1; /* no circ yet */
if (conn->_base.marked_for_close) {
/* This call will warn as appropriate. */
_connection_mark_for_close(TO_CONN(conn), line, file);
return;
}
if (!conn->socks_request->has_finished) {
if (endreason & END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED)
log_warn(LD_BUG,
"stream (marked at %s:%d) sending two socks replies?",
file, line);
if (SOCKS_COMMAND_IS_CONNECT(conn->socks_request->command))
connection_ap_handshake_socks_reply(conn, NULL, 0, endreason);
else if (SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command))
connection_ap_handshake_socks_resolved(conn,
RESOLVED_TYPE_ERROR_TRANSIENT,
0, NULL, -1, -1);
else /* unknown or no handshake at all. send no response. */
conn->socks_request->has_finished = 1;
}
_connection_mark_for_close(TO_CONN(conn), line, file);
conn->_base.hold_open_until_flushed = 1;
conn->end_reason = endreason;
}
/** There was an EOF. Send an end and mark the connection for close.
*/
int
connection_edge_reached_eof(edge_connection_t *conn)
{
if (buf_datalen(conn->_base.inbuf) &&
connection_state_is_open(TO_CONN(conn))) {
/* it still has stuff to process. don't let it die yet. */
return 0;
}
log_info(LD_EDGE,"conn (fd %d) reached eof. Closing.", conn->_base.s);
if (!conn->_base.marked_for_close) {
/* only mark it if not already marked. it's possible to
* get the 'end' right around when the client hangs up on us. */
connection_edge_end(conn, END_STREAM_REASON_DONE);
if (conn->socks_request) /* eof, so don't send a socks reply back */
conn->socks_request->has_finished = 1;
connection_mark_for_close(TO_CONN(conn));
}
return 0;
}
/** Handle new bytes on conn->inbuf based on state:
* - If it's waiting for socks info, try to read another step of the
* socks handshake out of conn->inbuf.
* - If it's waiting for the original destination, fetch it.
* - If it's open, then package more relay cells from the stream.
* - Else, leave the bytes on inbuf alone for now.
*
* Mark and return -1 if there was an unexpected error with the conn,
* else return 0.
*/
int
connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
{
tor_assert(conn);
switch (conn->_base.state) {
case AP_CONN_STATE_SOCKS_WAIT:
if (connection_ap_handshake_process_socks(conn) < 0) {
/* already marked */
return -1;
}
return 0;
case AP_CONN_STATE_NATD_WAIT:
if (connection_ap_process_natd(conn) < 0) {
/* already marked */
return -1;
}
return 0;
case AP_CONN_STATE_OPEN:
case EXIT_CONN_STATE_OPEN:
if (connection_edge_package_raw_inbuf(conn, package_partial) < 0) {
/* (We already sent an end cell if possible) */
connection_mark_for_close(TO_CONN(conn));
return -1;
}
return 0;
case EXIT_CONN_STATE_CONNECTING:
case AP_CONN_STATE_RENDDESC_WAIT:
case AP_CONN_STATE_CIRCUIT_WAIT:
case AP_CONN_STATE_CONNECT_WAIT:
case AP_CONN_STATE_RESOLVE_WAIT:
case AP_CONN_STATE_CONTROLLER_WAIT:
log_info(LD_EDGE,
"data from edge while in '%s' state. Leaving it on buffer.",
conn_state_to_string(conn->_base.type, conn->_base.state));
return 0;
}
log_warn(LD_BUG,"Got unexpected state %d. Closing.",conn->_base.state);
tor_fragile_assert();
connection_edge_end(conn, END_STREAM_REASON_INTERNAL);
connection_mark_for_close(TO_CONN(conn));
return -1;
}
/** This edge needs to be closed, because its circuit has closed.
* Mark it for close and return 0.
*/
int
connection_edge_destroy(uint16_t circ_id, edge_connection_t *conn)
{
if (!conn->_base.marked_for_close) {
log_info(LD_EDGE,
"CircID %d: At an edge. Marking connection for close.", circ_id);
if (conn->_base.type == CONN_TYPE_AP) {
connection_mark_unattached_ap(conn, END_STREAM_REASON_DESTROY);
} else {
/* closing the circuit, nothing to send an END to */
conn->_base.edge_has_sent_end = 1;
conn->end_reason = END_STREAM_REASON_DESTROY;
conn->end_reason |= END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED;
if (conn->_base.type == CONN_TYPE_AP)
control_event_stream_status(conn, STREAM_EVENT_CLOSED,
END_STREAM_REASON_DESTROY);
connection_mark_for_close(TO_CONN(conn));
conn->_base.hold_open_until_flushed = 1;
}
}
conn->cpath_layer = NULL;
conn->on_circuit = NULL;
return 0;
}
/** Send a relay end cell from stream <b>conn</b> down conn's circuit. Set
* the relay end cell's reason for closing as <b>reason</b>.
*
* Return -1 if this function has already been called on this conn,
* else return 0.
*/
int
connection_edge_end(edge_connection_t *conn, char reason)
{
char payload[RELAY_PAYLOAD_SIZE];
size_t payload_len=1;
circuit_t *circ;
if (conn->_base.edge_has_sent_end) {
log_warn(LD_BUG,"(Harmless.) Calling connection_edge_end (reason %d) "
"on an already ended stream?", reason);
tor_fragile_assert();
return -1;
}
if (conn->_base.marked_for_close) {
log_warn(LD_BUG,
"called on conn that's already marked for close at %s:%d.",
conn->_base.marked_for_close_file, conn->_base.marked_for_close);
return 0;
}
payload[0] = reason;
if (reason == END_STREAM_REASON_EXITPOLICY &&
!connection_edge_is_rendezvous_stream(conn)) {
set_uint32(payload+1, htonl(conn->_base.addr));
set_uint32(payload+5, htonl(dns_clip_ttl(conn->address_ttl)));
payload_len += 8;
}
circ = circuit_get_by_edge_conn(conn);
if (circ && !circ->marked_for_close) {
log_debug(LD_EDGE,"Sending end on conn (fd %d).",conn->_base.s);
connection_edge_send_command(conn, RELAY_COMMAND_END,
payload, payload_len);
} else {
log_debug(LD_EDGE,"No circ to send end on conn (fd %d).",
conn->_base.s);
}
conn->_base.edge_has_sent_end = 1;
conn->end_reason = reason;
return 0;
}
/** An error has just occured on an operation on an edge connection
* <b>conn</b>. Extract the errno; convert it to an end reason, and send an
* appropriate relay end cell to the other end of the connection's circuit.
**/
int
connection_edge_end_errno(edge_connection_t *conn)
{
uint8_t reason;
tor_assert(conn);
reason = (uint8_t)errno_to_end_reason(tor_socket_errno(conn->_base.s));
return connection_edge_end(conn, reason);
}
/** Connection <b>conn</b> has finished writing and has no bytes left on
* its outbuf.
*
* If it's in state 'open', stop writing, consider responding with a
* sendme, and return.
* Otherwise, stop writing and return.
*
* If <b>conn</b> is broken, mark it for close and return -1, else
* return 0.
*/
int
connection_edge_finished_flushing(edge_connection_t *conn)
{
tor_assert(conn);
switch (conn->_base.state) {
case AP_CONN_STATE_OPEN:
case EXIT_CONN_STATE_OPEN:
connection_stop_writing(TO_CONN(conn));
connection_edge_consider_sending_sendme(conn);
return 0;
case AP_CONN_STATE_SOCKS_WAIT:
case AP_CONN_STATE_NATD_WAIT:
case AP_CONN_STATE_RENDDESC_WAIT:
case AP_CONN_STATE_CIRCUIT_WAIT:
case AP_CONN_STATE_CONNECT_WAIT:
case AP_CONN_STATE_CONTROLLER_WAIT:
connection_stop_writing(TO_CONN(conn));
return 0;
default:
log_warn(LD_BUG, "Called in unexpected state %d.",conn->_base.state);
tor_fragile_assert();
return -1;
}
return 0;
}
/** Connected handler for exit connections: start writing pending
* data, deliver 'CONNECTED' relay cells as appropriate, and check
* any pending data that may have been received. */
int
connection_edge_finished_connecting(edge_connection_t *edge_conn)
{
char valbuf[INET_NTOA_BUF_LEN];
connection_t *conn;
struct in_addr in;
tor_assert(edge_conn);
tor_assert(edge_conn->_base.type == CONN_TYPE_EXIT);
conn = TO_CONN(edge_conn);
tor_assert(conn->state == EXIT_CONN_STATE_CONNECTING);
in.s_addr = htonl(conn->addr);
tor_inet_ntoa(&in,valbuf,sizeof(valbuf));
log_info(LD_EXIT,"Exit connection to %s:%u (%s) established.",
escaped_safe_str(conn->address),conn->port,safe_str(valbuf));
conn->state = EXIT_CONN_STATE_OPEN;
connection_watch_events(conn, EV_READ); /* stop writing, continue reading */
if (connection_wants_to_flush(conn)) /* in case there are any queued relay
* cells */
connection_start_writing(conn);
/* deliver a 'connected' relay cell back through the circuit. */
if (connection_edge_is_rendezvous_stream(edge_conn)) {
if (connection_edge_send_command(edge_conn,
RELAY_COMMAND_CONNECTED, NULL, 0) < 0)
return 0; /* circuit is closed, don't continue */
} else {
char connected_payload[8];
set_uint32(connected_payload, htonl(conn->addr));
set_uint32(connected_payload+4,
htonl(dns_clip_ttl(edge_conn->address_ttl)));
if (connection_edge_send_command(edge_conn,
RELAY_COMMAND_CONNECTED,
connected_payload, 8) < 0)
return 0; /* circuit is closed, don't continue */
}
tor_assert(edge_conn->package_window > 0);
/* in case the server has written anything */
return connection_edge_process_inbuf(edge_conn, 1);
}
/** Define a schedule for how long to wait between retrying
* application connections. Rather than waiting a fixed amount of
* time between each retry, we wait 10 seconds each for the first
* two tries, and 15 seconds for each retry after
* that. Hopefully this will improve the expected user experience. */
static int
compute_retry_timeout(edge_connection_t *conn)
{
if (conn->num_socks_retries < 2) /* try 0 and try 1 */
return 10;
return 15;
}
/** Find all general-purpose AP streams waiting for a response that sent their
* begin/resolve cell >=15 seconds ago. Detach from their current circuit, and
* mark their current circuit as unsuitable for new streams. Then call
* connection_ap_handshake_attach_circuit() to attach to a new circuit (if
* available) or launch a new one.
*
* For rendezvous streams, simply give up after SocksTimeout seconds (with no
* retry attempt).
*/
void
connection_ap_expire_beginning(void)
{
edge_connection_t *conn;
circuit_t *circ;
time_t now = time(NULL);
or_options_t *options = get_options();
int severity;
int cutoff;
int seconds_idle;
smartlist_t *conns = get_connection_array();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -