📄 connection.c
字号:
}
if (conn->type == CONN_TYPE_OR &&
!tor_digest_is_zero(TO_OR_CONN(conn)->identity_digest)) {
log_warn(LD_BUG, "called on OR conn with non-zeroed identity_digest");
connection_or_remove_from_identity_map(TO_OR_CONN(conn));
}
memset(conn, 0xAA, memlen); /* poison memory */
tor_free(mem);
}
/** Make sure <b>conn</b> isn't in any of the global conn lists; then free it.
*/
void
connection_free(connection_t *conn)
{
tor_assert(conn);
tor_assert(!connection_is_on_closeable_list(conn));
tor_assert(!connection_in_array(conn));
if (conn->linked_conn) {
log_err(LD_BUG, "Called with conn->linked_conn still set.");
tor_fragile_assert();
conn->linked_conn->linked_conn = NULL;
if (! conn->linked_conn->marked_for_close &&
conn->linked_conn->reading_from_linked_conn)
connection_start_reading(conn->linked_conn);
conn->linked_conn = NULL;
}
if (connection_speaks_cells(conn)) {
if (!tor_digest_is_zero(TO_OR_CONN(conn)->identity_digest)) {
connection_or_remove_from_identity_map(TO_OR_CONN(conn));
}
}
if (conn->type == CONN_TYPE_CONTROL) {
TO_CONTROL_CONN(conn)->event_mask = 0;
control_update_global_event_mask();
}
connection_unregister_events(conn);
_connection_free(conn);
}
/** Call _connection_free() on every connection in our array, and release all
* storage helpd by connection.c. This is used by cpuworkers and dnsworkers
* when they fork, so they don't keep resources held open (especially
* sockets).
*
* Don't do the checks in connection_free(), because they will
* fail.
*/
void
connection_free_all(void)
{
smartlist_t *conns = get_connection_array();
/* We don't want to log any messages to controllers. */
SMARTLIST_FOREACH(conns, connection_t *, conn,
if (conn->type == CONN_TYPE_CONTROL)
TO_CONTROL_CONN(conn)->event_mask = 0);
control_update_global_event_mask();
/* Unlink everything from the identity map. */
connection_or_clear_identity_map();
SMARTLIST_FOREACH(conns, connection_t *, conn, _connection_free(conn));
if (outgoing_addrs) {
SMARTLIST_FOREACH(outgoing_addrs, void*, addr, tor_free(addr));
smartlist_free(outgoing_addrs);
outgoing_addrs = NULL;
}
}
/** Do any cleanup needed:
* - Directory conns that failed to fetch a rendezvous descriptor
* need to inform pending rendezvous streams.
* - OR conns need to call rep_hist_note_*() to record status.
* - AP conns need to send a socks reject if necessary.
* - Exit conns need to call connection_dns_remove() if necessary.
* - AP and Exit conns need to send an end cell if they can.
* - DNS conns need to fail any resolves that are pending on them.
* - OR and edge connections need to be unlinked from circuits.
*/
void
connection_about_to_close_connection(connection_t *conn)
{
circuit_t *circ;
dir_connection_t *dir_conn;
or_connection_t *or_conn;
edge_connection_t *edge_conn;
time_t now = time(NULL);
tor_assert(conn->marked_for_close);
if (CONN_IS_EDGE(conn)) {
if (!conn->edge_has_sent_end) {
log_warn(LD_BUG, "(Harmless.) Edge connection (marked at %s:%d) "
"hasn't sent end yet?",
conn->marked_for_close_file, conn->marked_for_close);
tor_fragile_assert();
}
}
switch (conn->type) {
case CONN_TYPE_DIR:
dir_conn = TO_DIR_CONN(conn);
if (conn->state < DIR_CONN_STATE_CLIENT_FINISHED) {
/* It's a directory connection and connecting or fetching
* failed: forget about this router, and maybe try again. */
connection_dir_request_failed(dir_conn);
}
if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC)
rend_client_desc_here(dir_conn->rend_query); /* give it a try */
/* If we were trying to fetch a v2 rend desc and did not succeed,
* retry as needed. (If a fetch is successful, the connection state
* is changed to DIR_PURPOSE_HAS_FETCHED_RENDDESC to mark that
* refetching is unnecessary.) */
if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC_V2 &&
dir_conn->rend_query &&
strlen(dir_conn->rend_query) == REND_SERVICE_ID_LEN_BASE32)
rend_client_refetch_v2_renddesc(dir_conn->rend_query);
break;
case CONN_TYPE_OR:
or_conn = TO_OR_CONN(conn);
/* Remember why we're closing this connection. */
if (conn->state != OR_CONN_STATE_OPEN) {
if (connection_or_nonopen_was_started_here(or_conn)) {
rep_hist_note_connect_failed(or_conn->identity_digest, now);
entry_guard_register_connect_status(or_conn->identity_digest,0,now);
router_set_status(or_conn->identity_digest, 0);
control_event_or_conn_status(or_conn, OR_CONN_EVENT_FAILED,
control_tls_error_to_reason(or_conn->tls_error));
}
/* Inform any pending (not attached) circs that they should
* give up. */
circuit_n_conn_done(TO_OR_CONN(conn), 0);
} else if (conn->hold_open_until_flushed) {
/* We only set hold_open_until_flushed when we're intentionally
* closing a connection. */
rep_hist_note_disconnect(or_conn->identity_digest, now);
control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED,
control_tls_error_to_reason(or_conn->tls_error));
} else if (or_conn->identity_digest) {
rep_hist_note_connection_died(or_conn->identity_digest, now);
control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED,
control_tls_error_to_reason(or_conn->tls_error));
}
/* Now close all the attached circuits on it. */
circuit_unlink_all_from_or_conn(TO_OR_CONN(conn),
END_CIRC_REASON_OR_CONN_CLOSED);
break;
case CONN_TYPE_AP:
edge_conn = TO_EDGE_CONN(conn);
if (edge_conn->socks_request->has_finished == 0) {
/* since conn gets removed right after this function finishes,
* there's no point trying to send back a reply at this point. */
log_warn(LD_BUG,"Closing stream (marked at %s:%d) without sending"
" back a socks reply.",
conn->marked_for_close_file, conn->marked_for_close);
}
if (!edge_conn->end_reason) {
log_warn(LD_BUG,"Closing stream (marked at %s:%d) without having"
" set end_reason.",
conn->marked_for_close_file, conn->marked_for_close);
}
if (edge_conn->dns_server_request) {
log_warn(LD_BUG,"Closing stream (marked at %s:%d) without having"
" replied to DNS request.",
conn->marked_for_close_file, conn->marked_for_close);
dnsserv_reject_request(edge_conn);
}
control_event_stream_status(edge_conn, STREAM_EVENT_CLOSED,
edge_conn->end_reason);
circ = circuit_get_by_edge_conn(edge_conn);
if (circ)
circuit_detach_stream(circ, edge_conn);
break;
case CONN_TYPE_EXIT:
edge_conn = TO_EDGE_CONN(conn);
circ = circuit_get_by_edge_conn(edge_conn);
if (circ)
circuit_detach_stream(circ, edge_conn);
if (conn->state == EXIT_CONN_STATE_RESOLVING) {
connection_dns_remove(edge_conn);
}
break;
}
}
/** Return true iff connection_close_immediate() has been called on this
* connection. */
#define CONN_IS_CLOSED(c) \
((c)->linked ? ((c)->linked_conn_is_closed) : ((c)->s < 0))
/** Close the underlying socket for <b>conn</b>, so we don't try to
* flush it. Must be used in conjunction with (right before)
* connection_mark_for_close().
*/
void
connection_close_immediate(connection_t *conn)
{
assert_connection_ok(conn,0);
if (CONN_IS_CLOSED(conn)) {
log_err(LD_BUG,"Attempt to close already-closed connection.");
tor_fragile_assert();
return;
}
if (conn->outbuf_flushlen) {
log_info(LD_NET,"fd %d, type %s, state %s, %d bytes on outbuf.",
conn->s, conn_type_to_string(conn->type),
conn_state_to_string(conn->type, conn->state),
(int)conn->outbuf_flushlen);
}
connection_unregister_events(conn);
if (conn->s >= 0)
tor_close_socket(conn->s);
conn->s = -1;
if (conn->linked)
conn->linked_conn_is_closed = 1;
if (!connection_is_listener(conn)) {
buf_clear(conn->outbuf);
conn->outbuf_flushlen = 0;
}
}
/** Mark <b>conn</b> to be closed next time we loop through
* conn_close_if_marked() in main.c. */
void
_connection_mark_for_close(connection_t *conn, int line, const char *file)
{
assert_connection_ok(conn,0);
tor_assert(line);
tor_assert(line < 1<<16); /* marked_for_close can only fit a uint16_t. */
tor_assert(file);
if (conn->marked_for_close) {
log(LOG_WARN,LD_BUG,"Duplicate call to connection_mark_for_close at %s:%d"
" (first at %s:%d)", file, line, conn->marked_for_close_file,
conn->marked_for_close);
tor_fragile_assert();
return;
}
conn->marked_for_close = line;
conn->marked_for_close_file = file;
add_connection_to_closeable_list(conn);
/* in case we're going to be held-open-til-flushed, reset
* the number of seconds since last successful write, so
* we get our whole 15 seconds */
conn->timestamp_lastwritten = time(NULL);
}
/** Find each connection that has hold_open_until_flushed set to
* 1 but hasn't written in the past 15 seconds, and set
* hold_open_until_flushed to 0. This means it will get cleaned
* up in the next loop through close_if_marked() in main.c.
*/
void
connection_expire_held_open(void)
{
time_t now;
smartlist_t *conns = get_connection_array();
now = time(NULL);
SMARTLIST_FOREACH(conns, connection_t *, conn,
{
/* If we've been holding the connection open, but we haven't written
* for 15 seconds...
*/
if (conn->hold_open_until_flushed) {
tor_assert(conn->marked_for_close);
if (now - conn->timestamp_lastwritten >= 15) {
int severity;
if (conn->type == CONN_TYPE_EXIT ||
(conn->type == CONN_TYPE_DIR &&
conn->purpose == DIR_PURPOSE_SERVER))
severity = LOG_INFO;
else
severity = LOG_NOTICE;
log_fn(severity, LD_NET,
"Giving up on marked_for_close conn that's been flushing "
"for 15s (fd %d, type %s, state %s).",
conn->s, conn_type_to_string(conn->type),
conn_state_to_string(conn->type, conn->state));
conn->hold_open_until_flushed = 0;
}
}
});
}
/** Create an AF_INET listenaddr struct.
* <b>listenaddress</b> provides the host and optionally the port information
* for the new structure. If no port is provided in <b>listenaddress</b> then
* <b>listenport</b> is used.
*
* If not NULL <b>readable_addrress</b> will contain a copy of the host part of
* <b>listenaddress</b>.
*
* The listenaddr struct has to be freed by the caller.
*/
static struct sockaddr_in *
create_inet_sockaddr(const char *listenaddress, uint16_t listenport,
char **readable_address) {
struct sockaddr_in *listenaddr = NULL;
uint32_t addr;
uint16_t usePort = 0;
if (parse_addr_port(LOG_WARN,
listenaddress, readable_address, &addr, &usePort)<0) {
log_warn(LD_CONFIG,
"Error parsing/resolving ListenAddress %s", listenaddress);
goto err;
}
if (usePort==0)
usePort = listenport;
listenaddr = tor_malloc_zero(sizeof(struct sockaddr_in));
listenaddr->sin_addr.s_addr = htonl(addr);
listenaddr->sin_family = AF_INET;
listenaddr->sin_port = htons((uint16_t) usePort);
return listenaddr;
err:
tor_free(listenaddr);
return NULL;
}
#ifdef HAVE_SYS_UN_H
/** Create an AF_UNIX listenaddr struct.
* <b>listenaddress</b> provides the path to the unix socket.
*
* Eventually <b>listenaddress</b> will also optionally contain user, group,
* and file permissions for the new socket. But not yet. XXX
* Also, since we do not create the socket here the information doesn't help
* here.
*
* If not NULL <b>readable_addrress</b> will contain a copy of the path part of
* <b>listenaddress</b>.
*
* The listenaddr struct has to be freed by the caller.
*/
static struct sockaddr_un *
create_unix_sockaddr(const char *listenaddress, char **readable_address)
{
struct sockaddr_un *sockaddr = NULL;
sockaddr = tor_malloc_zero(sizeof(struct sockaddr_un));
sockaddr->sun_family = AF_UNIX;
strncpy(sockaddr->sun_path, listenaddress, sizeof(sockaddr->sun_path));
if (readable_address)
*readable_address = tor_strdup(listenaddress);
return sockaddr;
}
#else
static struct sockaddr *
create_unix_sockaddr(const char *listenaddress, char **readable_address)
{
(void)listenaddress;
(void)readable_address;
log_fn(LOG_ERR, LD_BUG,
"Unix domain sockets not supported, yet we tried to create one.");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -