📄 connection.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_c_id[] =
"$Id$";
/**
* \file connection.c
* \brief General high-level functions to handle reading and writing
* on connections.
**/
#include "or.h"
static connection_t *connection_create_listener(
struct sockaddr *listensockaddr, int type,
char* address);
static int connection_init_accepted_conn(connection_t *conn,
uint8_t listener_type);
static int connection_handle_listener_read(connection_t *conn, int new_type);
static int connection_read_bucket_should_increase(or_connection_t *conn);
static int connection_finished_flushing(connection_t *conn);
static int connection_flushed_some(connection_t *conn);
static int connection_finished_connecting(connection_t *conn);
static int connection_reached_eof(connection_t *conn);
static int connection_read_to_buf(connection_t *conn, int *max_to_read);
static int connection_process_inbuf(connection_t *conn, int package_partial);
static void client_check_address_changed(int sock);
static void set_constrained_socket_buffers(int sock, int size);
static uint32_t last_interface_ip = 0;
static smartlist_t *outgoing_addrs = NULL;
/**************************************************************/
/**
* Return the human-readable name for the connection type <b>type</b>
*/
const char *
conn_type_to_string(int type)
{
static char buf[64];
switch (type) {
case CONN_TYPE_OR_LISTENER: return "OR listener";
case CONN_TYPE_OR: return "OR";
case CONN_TYPE_EXIT: return "Exit";
case CONN_TYPE_AP_LISTENER: return "Socks listener";
case CONN_TYPE_AP_TRANS_LISTENER:
return "Transparent pf/netfilter listener";
case CONN_TYPE_AP_NATD_LISTENER: return "Transparent natd listener";
case CONN_TYPE_AP_DNS_LISTENER: return "DNS listener";
case CONN_TYPE_AP: return "Socks";
case CONN_TYPE_DIR_LISTENER: return "Directory listener";
case CONN_TYPE_DIR: return "Directory";
case CONN_TYPE_CPUWORKER: return "CPU worker";
case CONN_TYPE_CONTROL_LISTENER: return "Control listener";
case CONN_TYPE_CONTROL: return "Control";
default:
log_warn(LD_BUG, "unknown connection type %d", type);
tor_snprintf(buf, sizeof(buf), "unknown [%d]", type);
return buf;
}
}
/**
* Return the human-readable name for the connection state <b>state</b>
* for the connection type <b>type</b>
*/
const char *
conn_state_to_string(int type, int state)
{
static char buf[96];
switch (type) {
case CONN_TYPE_OR_LISTENER:
case CONN_TYPE_AP_LISTENER:
case CONN_TYPE_AP_TRANS_LISTENER:
case CONN_TYPE_AP_NATD_LISTENER:
case CONN_TYPE_AP_DNS_LISTENER:
case CONN_TYPE_DIR_LISTENER:
case CONN_TYPE_CONTROL_LISTENER:
if (state == LISTENER_STATE_READY)
return "ready";
break;
case CONN_TYPE_OR:
switch (state) {
case OR_CONN_STATE_CONNECTING: return "connect()ing";
case OR_CONN_STATE_PROXY_FLUSHING: return "proxy flushing";
case OR_CONN_STATE_PROXY_READING: return "proxy reading";
case OR_CONN_STATE_TLS_HANDSHAKING: return "handshaking (TLS)";
case OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING:
return "renegotiating (TLS)";
case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING:
return "waiting for renegotiation (TLS)";
case OR_CONN_STATE_OR_HANDSHAKING: return "handshaking (Tor)";
case OR_CONN_STATE_OPEN: return "open";
}
break;
case CONN_TYPE_EXIT:
switch (state) {
case EXIT_CONN_STATE_RESOLVING: return "waiting for dest info";
case EXIT_CONN_STATE_CONNECTING: return "connecting";
case EXIT_CONN_STATE_OPEN: return "open";
case EXIT_CONN_STATE_RESOLVEFAILED: return "resolve failed";
}
break;
case CONN_TYPE_AP:
switch (state) {
case AP_CONN_STATE_SOCKS_WAIT: return "waiting for socks info";
case AP_CONN_STATE_NATD_WAIT: return "waiting for natd dest info";
case AP_CONN_STATE_RENDDESC_WAIT: return "waiting for rendezvous desc";
case AP_CONN_STATE_CONTROLLER_WAIT: return "waiting for controller";
case AP_CONN_STATE_CIRCUIT_WAIT: return "waiting for circuit";
case AP_CONN_STATE_CONNECT_WAIT: return "waiting for connect response";
case AP_CONN_STATE_RESOLVE_WAIT: return "waiting for resolve response";
case AP_CONN_STATE_OPEN: return "open";
}
break;
case CONN_TYPE_DIR:
switch (state) {
case DIR_CONN_STATE_CONNECTING: return "connecting";
case DIR_CONN_STATE_CLIENT_SENDING: return "client sending";
case DIR_CONN_STATE_CLIENT_READING: return "client reading";
case DIR_CONN_STATE_CLIENT_FINISHED: return "client finished";
case DIR_CONN_STATE_SERVER_COMMAND_WAIT: return "waiting for command";
case DIR_CONN_STATE_SERVER_WRITING: return "writing";
}
break;
case CONN_TYPE_CPUWORKER:
switch (state) {
case CPUWORKER_STATE_IDLE: return "idle";
case CPUWORKER_STATE_BUSY_ONION: return "busy with onion";
}
break;
case CONN_TYPE_CONTROL:
switch (state) {
case CONTROL_CONN_STATE_OPEN: return "open (protocol v1)";
case CONTROL_CONN_STATE_NEEDAUTH:
return "waiting for authentication (protocol v1)";
}
break;
}
log_warn(LD_BUG, "unknown connection state %d (type %d)", state, type);
tor_snprintf(buf, sizeof(buf),
"unknown state [%d] on unknown [%s] connection",
state, conn_type_to_string(type));
return buf;
}
/** Allocate space for a new connection_t. This function just initializes
* conn; you must call connection_add() to link it into the main array.
*
* Set conn-\>type to <b>type</b>. Set conn-\>s and conn-\>conn_array_index to
* -1 to signify they are not yet assigned.
*
* If conn is not a listener type, allocate buffers for it. If it's
* an AP type, allocate space to store the socks_request.
*
* Assign a pseudorandom next_circ_id between 0 and 2**15.
*
* Initialize conn's timestamps to now.
*/
connection_t *
connection_new(int type, int socket_family)
{
static uint32_t n_connections_allocated = 1;
connection_t *conn;
time_t now = time(NULL);
size_t length;
uint32_t magic;
switch (type) {
case CONN_TYPE_OR:
length = sizeof(or_connection_t);
magic = OR_CONNECTION_MAGIC;
break;
case CONN_TYPE_EXIT:
case CONN_TYPE_AP:
length = sizeof(edge_connection_t);
magic = EDGE_CONNECTION_MAGIC;
break;
case CONN_TYPE_DIR:
length = sizeof(dir_connection_t);
magic = DIR_CONNECTION_MAGIC;
break;
case CONN_TYPE_CONTROL:
length = sizeof(control_connection_t);
magic = CONTROL_CONNECTION_MAGIC;
break;
default:
length = sizeof(connection_t);
magic = BASE_CONNECTION_MAGIC;
break;
}
conn = tor_malloc_zero(length);
conn->magic = magic;
conn->s = -1; /* give it a default of 'not used' */
conn->conn_array_index = -1; /* also default to 'not used' */
conn->type = type;
conn->socket_family = socket_family;
if (!connection_is_listener(conn)) { /* listeners never use their buf */
conn->inbuf = buf_new();
conn->outbuf = buf_new();
}
if (type == CONN_TYPE_AP) {
TO_EDGE_CONN(conn)->socks_request =
tor_malloc_zero(sizeof(socks_request_t));
}
if (CONN_IS_EDGE(conn)) {
TO_EDGE_CONN(conn)->global_identifier = n_connections_allocated++;
}
if (type == CONN_TYPE_OR) {
TO_OR_CONN(conn)->timestamp_last_added_nonpadding = now;
TO_OR_CONN(conn)->next_circ_id = crypto_rand_int(1<<15);
}
conn->timestamp_created = now;
conn->timestamp_lastread = now;
conn->timestamp_lastwritten = now;
return conn;
}
/** Create a link between <b>conn_a</b> and <b>conn_b</b>. */
void
connection_link_connections(connection_t *conn_a, connection_t *conn_b)
{
tor_assert(conn_a->s < 0);
tor_assert(conn_b->s < 0);
conn_a->linked = 1;
conn_b->linked = 1;
conn_a->linked_conn = conn_b;
conn_b->linked_conn = conn_a;
}
/** Tell libevent that we don't care about <b>conn</b> any more. */
void
connection_unregister_events(connection_t *conn)
{
if (conn->read_event) {
if (event_del(conn->read_event))
log_warn(LD_BUG, "Error removing read event for %d", conn->s);
tor_free(conn->read_event);
}
if (conn->write_event) {
if (event_del(conn->write_event))
log_warn(LD_BUG, "Error removing write event for %d", conn->s);
tor_free(conn->write_event);
}
if (conn->dns_server_port) {
dnsserv_close_listener(conn);
}
}
/** Deallocate memory used by <b>conn</b>. Deallocate its buffers if
* necessary, close its socket if necessary, and mark the directory as dirty
* if <b>conn</b> is an OR or OP connection.
*/
static void
_connection_free(connection_t *conn)
{
void *mem;
size_t memlen;
switch (conn->type) {
case CONN_TYPE_OR:
tor_assert(conn->magic == OR_CONNECTION_MAGIC);
mem = TO_OR_CONN(conn);
memlen = sizeof(or_connection_t);
break;
case CONN_TYPE_AP:
case CONN_TYPE_EXIT:
tor_assert(conn->magic == EDGE_CONNECTION_MAGIC);
mem = TO_EDGE_CONN(conn);
memlen = sizeof(edge_connection_t);
break;
case CONN_TYPE_DIR:
tor_assert(conn->magic == DIR_CONNECTION_MAGIC);
mem = TO_DIR_CONN(conn);
memlen = sizeof(dir_connection_t);
break;
case CONN_TYPE_CONTROL:
tor_assert(conn->magic == CONTROL_CONNECTION_MAGIC);
mem = TO_CONTROL_CONN(conn);
memlen = sizeof(control_connection_t);
break;
default:
tor_assert(conn->magic == BASE_CONNECTION_MAGIC);
mem = conn;
memlen = sizeof(connection_t);
break;
}
if (conn->linked) {
log_info(LD_GENERAL, "Freeing linked %s connection [%s] with %d "
"bytes on inbuf, %d on outbuf.",
conn_type_to_string(conn->type),
conn_state_to_string(conn->type, conn->state),
(int)buf_datalen(conn->inbuf), (int)buf_datalen(conn->outbuf));
}
if (!connection_is_listener(conn)) {
buf_free(conn->inbuf);
buf_free(conn->outbuf);
} else {
if (conn->socket_family == AF_UNIX) {
/* For now only control ports can be unix domain sockets
* and listeners at the same time */
tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER);
if (unlink(conn->address) < 0 && errno != ENOENT) {
log_warn(LD_NET, "Could not unlink %s: %s", conn->address,
strerror(errno));
}
}
}
tor_free(conn->address);
if (connection_speaks_cells(conn)) {
or_connection_t *or_conn = TO_OR_CONN(conn);
if (or_conn->tls) {
tor_tls_free(or_conn->tls);
or_conn->tls = NULL;
}
if (or_conn->handshake_state) {
or_handshake_state_free(or_conn->handshake_state);
or_conn->handshake_state = NULL;
}
tor_free(or_conn->nickname);
}
if (CONN_IS_EDGE(conn)) {
edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
tor_free(edge_conn->chosen_exit_name);
if (edge_conn->socks_request) {
memset(edge_conn->socks_request, 0xcc, sizeof(socks_request_t));
tor_free(edge_conn->socks_request);
}
}
if (conn->type == CONN_TYPE_CONTROL) {
control_connection_t *control_conn = TO_CONTROL_CONN(conn);
tor_free(control_conn->incoming_cmd);
}
tor_free(conn->read_event); /* Probably already freed by connection_free. */
tor_free(conn->write_event); /* Probably already freed by connection_free. */
if (conn->type == CONN_TYPE_DIR) {
dir_connection_t *dir_conn = TO_DIR_CONN(conn);
tor_free(dir_conn->requested_resource);
if (dir_conn->zlib_state)
tor_zlib_free(dir_conn->zlib_state);
if (dir_conn->fingerprint_stack) {
SMARTLIST_FOREACH(dir_conn->fingerprint_stack, char *, cp, tor_free(cp));
smartlist_free(dir_conn->fingerprint_stack);
}
if (dir_conn->cached_dir)
cached_dir_decref(dir_conn->cached_dir);
}
if (conn->s >= 0) {
log_debug(LD_NET,"closing fd %d.",conn->s);
tor_close_socket(conn->s);
conn->s = -1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -