Add ORCONN event pubsub system

Add a publish-subscribe subsystem to publish messages about changes to
OR connections.

connection_or_change_state() in connection_or.c and
control_event_or_conn_event() in control.c publish messages to this
subsystem via helper functions.

Move state constants from connection_or.h to orconn_state.h so that
subscribers don't have to include all of connection_or.h to take
actions based on changes in OR connection state.  Move event constants
from control.h for similar reasons.

Part of ticket 27167.
This commit is contained in:
Taylor Yu
2018-12-16 16:05:58 -06:00
parent 308dde0c38
commit 271b50f54a
10 changed files with 288 additions and 49 deletions
+2
View File
@@ -8,6 +8,7 @@
#include "lib/cc/compat_compiler.h"
#include "lib/cc/torint.h"
#include "core/or/orconn_event_sys.h"
#include "lib/compress/compress_sys.h"
#include "lib/crypt_ops/crypto_sys.h"
#include "lib/err/torerr_sys.h"
@@ -35,6 +36,7 @@ const subsys_fns_t *tor_subsystems[] = {
&sys_compress, /* -70 */
&sys_crypto, /* -60 */
&sys_tortls, /* -50 */
&sys_orconn_event, /* -40 */
};
const unsigned n_tor_subsystems = ARRAY_LENGTH(tor_subsystems);
+3
View File
@@ -39,6 +39,7 @@ LIBTOR_APP_A_SOURCES = \
src/core/or/connection_or.c \
src/core/or/dos.c \
src/core/or/onion.c \
src/core/or/orconn_event.c \
src/core/or/policies.c \
src/core/or/protover.c \
src/core/or/protover_rust.c \
@@ -238,6 +239,8 @@ noinst_HEADERS += \
src/core/or/listener_connection_st.h \
src/core/or/onion.h \
src/core/or/or.h \
src/core/or/orconn_event.h \
src/core/or/orconn_event_sys.h \
src/core/or/or_circuit_st.h \
src/core/or/or_connection_st.h \
src/core/or/or_handshake_certs_st.h \
+1 -1
View File
@@ -1875,7 +1875,7 @@ connection_init_accepted_conn(connection_t *conn,
/* Initiate Extended ORPort authentication. */
return connection_ext_or_start_auth(TO_OR_CONN(conn));
case CONN_TYPE_OR:
control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0);
connection_or_event_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0);
rv = connection_tls_start_handshake(TO_OR_CONN(conn), 1);
if (rv < 0) {
connection_or_close_for_error(TO_OR_CONN(conn), 0);
+62 -11
View File
@@ -29,6 +29,7 @@
*/
#define TOR_CHANNEL_INTERNAL_
#define CONNECTION_OR_PRIVATE
#define ORCONN_EVENT_PRIVATE
#include "core/or/channel.h"
#include "core/or/channeltls.h"
#include "core/or/circuitbuild.h"
@@ -79,6 +80,8 @@
#include "lib/tls/tortls.h"
#include "lib/tls/x509.h"
#include "core/or/orconn_event.h"
static int connection_tls_finish_handshake(or_connection_t *conn);
static int connection_or_launch_v3_or_handshake(or_connection_t *conn);
static int connection_or_process_cells_from_inbuf(or_connection_t *conn);
@@ -401,6 +404,49 @@ connection_or_report_broken_states(int severity, int domain)
smartlist_free(items);
}
/**
* Helper function to publish an OR connection status event
*
* Publishes a messages to subscribers of ORCONN messages, and sends
* the control event.
**/
void
connection_or_event_status(or_connection_t *conn, or_conn_status_event_t tp,
int reason)
{
orconn_event_msg_t msg;
msg.type = ORCONN_MSGTYPE_STATUS;
msg.u.status.gid = conn->base_.global_identifier;
msg.u.status.status = tp;
msg.u.status.reason = reason;
orconn_event_publish(&msg);
control_event_or_conn_status(conn, tp, reason);
}
/**
* Helper function to publish a state change message
*
* connection_or_change_state() calls this to notify subscribers about
* a change of an OR connection state.
**/
static void
connection_or_state_publish(const or_connection_t *conn, uint8_t state)
{
orconn_event_msg_t msg;
msg.type = ORCONN_MSGTYPE_STATE;
msg.u.state.gid = conn->base_.global_identifier;
msg.u.state.proxy_type = conn->proxy_type;
msg.u.state.state = state;
if (conn->chan) {
msg.u.state.chan = TLS_CHAN_TO_BASE(conn->chan)->global_identifier;
} else {
msg.u.state.chan = 0;
}
orconn_event_publish(&msg);
}
/** Call this to change or_connection_t states, so the owning channel_tls_t can
* be notified.
*/
@@ -412,6 +458,7 @@ connection_or_change_state(or_connection_t *conn, uint8_t state)
conn->base_.state = state;
connection_or_state_publish(conn, state);
if (conn->chan)
channel_tls_handle_state_change_on_orconn(conn->chan, conn, state);
}
@@ -755,8 +802,8 @@ connection_or_about_to_close(or_connection_t *or_conn)
entry_guard_chan_failed(TLS_CHAN_TO_BASE(or_conn->chan));
if (conn->state >= OR_CONN_STATE_TLS_HANDSHAKING) {
int reason = tls_error_to_orconn_end_reason(or_conn->tls_error);
control_event_or_conn_status(or_conn, OR_CONN_EVENT_FAILED,
reason);
connection_or_event_status(or_conn, OR_CONN_EVENT_FAILED,
reason);
if (!authdir_mode_tests_reachability(options))
control_event_bootstrap_prob_or(
orconn_end_reason_to_control_string(reason),
@@ -766,10 +813,10 @@ connection_or_about_to_close(or_connection_t *or_conn)
} else if (conn->hold_open_until_flushed) {
/* We only set hold_open_until_flushed when we're intentionally
* closing a connection. */
control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED,
connection_or_event_status(or_conn, OR_CONN_EVENT_CLOSED,
tls_error_to_orconn_end_reason(or_conn->tls_error));
} else if (!tor_digest_is_zero(or_conn->identity_digest)) {
control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED,
connection_or_event_status(or_conn, OR_CONN_EVENT_CLOSED,
tls_error_to_orconn_end_reason(or_conn->tls_error));
}
}
@@ -1361,7 +1408,7 @@ void
connection_or_connect_failed(or_connection_t *conn,
int reason, const char *msg)
{
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, reason);
connection_or_event_status(conn, OR_CONN_EVENT_FAILED, reason);
if (!authdir_mode_tests_reachability(get_options()))
control_event_bootstrap_prob_or(msg, reason, conn);
note_or_connect_failed(conn);
@@ -1468,9 +1515,6 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port,
return NULL;
}
connection_or_change_state(conn, OR_CONN_STATE_CONNECTING);
control_event_or_conn_status(conn, OR_CONN_EVENT_LAUNCHED, 0);
conn->is_outgoing = 1;
/* If we are using a proxy server, find it and use it. */
@@ -1482,7 +1526,14 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port,
port = proxy_port;
conn->base_.proxy_state = PROXY_INFANT;
}
connection_or_change_state(conn, OR_CONN_STATE_CONNECTING);
connection_or_event_status(conn, OR_CONN_EVENT_LAUNCHED, 0);
} else {
/* This duplication of state change calls is necessary in case we
* run into an error condition below */
connection_or_change_state(conn, OR_CONN_STATE_CONNECTING);
connection_or_event_status(conn, OR_CONN_EVENT_LAUNCHED, 0);
/* get_proxy_addrport() might fail if we have a Bridge line that
references a transport, but no ClientTransportPlugin lines
defining its transport proxy. If this is the case, let's try to
@@ -1978,8 +2029,8 @@ connection_or_client_learned_peer_id(or_connection_t *conn,
/* Tell the new guard API about the channel failure */
entry_guard_chan_failed(TLS_CHAN_TO_BASE(conn->chan));
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED,
END_OR_CONN_REASON_OR_IDENTITY);
connection_or_event_status(conn, OR_CONN_EVENT_FAILED,
END_OR_CONN_REASON_OR_IDENTITY);
if (!authdir_mode_tests_reachability(options))
control_event_bootstrap_prob_or(
"Unexpected identity in router certificate",
@@ -2219,7 +2270,7 @@ int
connection_or_set_state_open(or_connection_t *conn)
{
connection_or_change_state(conn, OR_CONN_STATE_OPEN);
control_event_or_conn_status(conn, OR_CONN_EVENT_CONNECTED, 0);
connection_or_event_status(conn, OR_CONN_EVENT_CONNECTED, 0);
/* Link protocol 3 appeared in Tor 0.2.3.6-alpha, so any connection
* that uses an earlier link protocol should not be treated as a relay. */
+4 -26
View File
@@ -17,32 +17,7 @@ struct ed25519_keypair_t;
or_connection_t *TO_OR_CONN(connection_t *);
#define OR_CONN_STATE_MIN_ 1
/** State for a connection to an OR: waiting for connect() to finish. */
#define OR_CONN_STATE_CONNECTING 1
/** State for a connection to an OR: waiting for proxy handshake to complete */
#define OR_CONN_STATE_PROXY_HANDSHAKING 2
/** State for an OR connection client: SSL is handshaking, not done
* yet. */
#define OR_CONN_STATE_TLS_HANDSHAKING 3
/** State for a connection to an OR: We're doing a second SSL handshake for
* renegotiation purposes. (V2 handshake only.) */
#define OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING 4
/** State for a connection at an OR: We're waiting for the client to
* renegotiate (to indicate a v2 handshake) or send a versions cell (to
* indicate a v3 handshake) */
#define OR_CONN_STATE_TLS_SERVER_RENEGOTIATING 5
/** State for an OR connection: We're done with our SSL handshake, we've done
* renegotiation, but we haven't yet negotiated link protocol versions and
* sent a netinfo cell. */
#define OR_CONN_STATE_OR_HANDSHAKING_V2 6
/** State for an OR connection: We're done with our SSL handshake, but we
* haven't yet negotiated link protocol versions, done a V3 handshake, and
* sent a netinfo cell. */
#define OR_CONN_STATE_OR_HANDSHAKING_V3 7
/** State for an OR connection: Ready to send/receive cells. */
#define OR_CONN_STATE_OPEN 8
#define OR_CONN_STATE_MAX_ 8
#include "core/or/orconn_event.h"
void connection_or_clear_identity(or_connection_t *conn);
void connection_or_clear_identity_map(void);
@@ -81,6 +56,9 @@ MOCK_DECL(void,connection_or_close_for_error,
void connection_or_report_broken_states(int severity, int domain);
void connection_or_event_status(or_connection_t *conn,
or_conn_status_event_t tp, int reason);
MOCK_DECL(int,connection_tls_start_handshake,(or_connection_t *conn,
int receiving));
int connection_tls_continue_handshake(or_connection_t *conn);
+81
View File
@@ -0,0 +1,81 @@
/* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file orconn_event.c
* \brief Publish state change messages for OR connections
*
* Implements a basic publish-subscribe framework for messages about
* the state of OR connections. The publisher calls the subscriber
* callback functions synchronously.
*
* Although the synchronous calls might not simplify the call graph,
* this approach improves data isolation because the publisher doesn't
* need knowledge about the internals of subscribing subsystems. It
* also avoids race conditions that might occur in asynchronous
* frameworks.
**/
#include "core/or/or.h"
#include "lib/subsys/subsys.h"
#define ORCONN_EVENT_PRIVATE
#include "core/or/orconn_event.h"
#include "core/or/orconn_event_sys.h"
/** List of subscribers */
static smartlist_t *orconn_event_rcvrs;
/** Initialize subscriber list */
static int
orconn_event_init(void)
{
orconn_event_rcvrs = smartlist_new();
return 0;
}
/** Free subscriber list */
static void
orconn_event_fini(void)
{
smartlist_free(orconn_event_rcvrs);
}
/**
* Subscribe to messages about OR connection events
*
* Register a callback function to receive messages about ORCONNs.
* The publisher calls this function synchronously.
**/
void
orconn_event_subscribe(orconn_event_rcvr_t fn)
{
tor_assert(fn);
/* Don't duplicate subscriptions. */
if (smartlist_contains(orconn_event_rcvrs, fn))
return;
smartlist_add(orconn_event_rcvrs, fn);
}
/**
* Publish a message about OR connection events
*
* This calls the subscriber receiver function synchronously.
**/
void
orconn_event_publish(const orconn_event_msg_t *msg)
{
SMARTLIST_FOREACH_BEGIN(orconn_event_rcvrs, orconn_event_rcvr_t, fn) {
tor_assert(fn);
(*fn)(msg);
} SMARTLIST_FOREACH_END(fn);
}
const subsys_fns_t sys_orconn_event = {
.name = "orconn_event",
.supported = true,
.level = -40,
.initialize = orconn_event_init,
.shutdown = orconn_event_fini,
};
+120
View File
@@ -0,0 +1,120 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file orconn_event.h
* \brief Header file for orconn_event.c
*
* The OR_CONN_STATE_* symbols are here to make it easier for
* subscribers to make decisions based on the messages that they
* receive.
**/
#ifndef TOR_ORCONN_EVENT_H
#define TOR_ORCONN_EVENT_H
/**
* @name States of OR connections
*
* These must be in a partial ordering such that usually no OR
* connection will transition from a higher-numbered state to a
* lower-numbered one. Code such as bto_update_best() depends on this
* ordering to determine the best state it's seen so far.
* @{ */
#define OR_CONN_STATE_MIN_ 1
/** State for a connection to an OR: waiting for connect() to finish. */
#define OR_CONN_STATE_CONNECTING 1
/** State for a connection to an OR: waiting for proxy handshake to complete */
#define OR_CONN_STATE_PROXY_HANDSHAKING 2
/** State for an OR connection client: SSL is handshaking, not done
* yet. */
#define OR_CONN_STATE_TLS_HANDSHAKING 3
/** State for a connection to an OR: We're doing a second SSL handshake for
* renegotiation purposes. (V2 handshake only.) */
#define OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING 4
/** State for a connection at an OR: We're waiting for the client to
* renegotiate (to indicate a v2 handshake) or send a versions cell (to
* indicate a v3 handshake) */
#define OR_CONN_STATE_TLS_SERVER_RENEGOTIATING 5
/** State for an OR connection: We're done with our SSL handshake, we've done
* renegotiation, but we haven't yet negotiated link protocol versions and
* sent a netinfo cell. */
#define OR_CONN_STATE_OR_HANDSHAKING_V2 6
/** State for an OR connection: We're done with our SSL handshake, but we
* haven't yet negotiated link protocol versions, done a V3 handshake, and
* sent a netinfo cell. */
#define OR_CONN_STATE_OR_HANDSHAKING_V3 7
/** State for an OR connection: Ready to send/receive cells. */
#define OR_CONN_STATE_OPEN 8
#define OR_CONN_STATE_MAX_ 8
/** @} */
/** Used to indicate the type of an OR connection event passed to the
* controller. The various types are defined in control-spec.txt */
typedef enum or_conn_status_event_t {
OR_CONN_EVENT_LAUNCHED = 0,
OR_CONN_EVENT_CONNECTED = 1,
OR_CONN_EVENT_FAILED = 2,
OR_CONN_EVENT_CLOSED = 3,
OR_CONN_EVENT_NEW = 4,
} or_conn_status_event_t;
/** Discriminant values for orconn event message */
typedef enum orconn_msgtype_t {
ORCONN_MSGTYPE_STATE,
ORCONN_MSGTYPE_STATUS,
} orconn_msgtype_t;
/**
* Message for orconn state update
*
* This contains information about internal state changes of
* or_connection_t objects. The chan and proxy_type fields are
* additional information that a subscriber may need to make
* decisions.
**/
typedef struct orconn_state_msg_t {
uint64_t gid; /**< connection's global ID */
uint64_t chan; /**< associated channel ID */
int proxy_type; /**< connection's proxy type */
uint8_t state; /**< new connection state */
} orconn_state_msg_t;
/**
* Message for orconn status event
*
* This contains information that ends up in ORCONN control protocol
* events.
**/
typedef struct orconn_status_msg_t {
uint64_t gid; /**< connection's global ID */
int status; /**< or_conn_status_event_t */
int reason; /**< reason */
} orconn_status_msg_t;
/** Discriminated union for the actual message */
typedef struct orconn_event_msg_t {
int type;
union {
orconn_state_msg_t state;
orconn_status_msg_t status;
} u;
} orconn_event_msg_t;
/**
* Receiver function pointer for OR subscribers
*
* This function gets called synchronously by the publisher.
**/
typedef void (*orconn_event_rcvr_t)(const orconn_event_msg_t *);
void orconn_event_subscribe(orconn_event_rcvr_t);
#ifdef ORCONN_EVENT_PRIVATE
void orconn_event_publish(const orconn_event_msg_t *);
#endif
#endif /* defined(TOR_ORCONN_EVENT_H) */
+12
View File
@@ -0,0 +1,12 @@
/* Copyright (c) 2007-2018, The Tor Project, Inc. */
/**
* \file orconn_event_sys.h
* \brief Declare subsystem object for the OR connection event module.
**/
#ifndef TOR_ORCONN_EVENT_SYS_H
#define TOR_ORCONN_EVENT_SYS_H
extern const struct subsys_fns_t sys_orconn_event;
#endif /* defined(TOR_ORCONN_SYS_H) */
+2 -10
View File
@@ -29,6 +29,8 @@ typedef enum circuit_status_minor_event_t {
CIRC_MINOR_EVENT_CANNIBALIZED,
} circuit_status_minor_event_t;
#include "core/or/orconn_event.h"
/** Used to indicate the type of a stream event passed to the controller.
* The various types are defined in control-spec.txt */
typedef enum stream_status_event_t {
@@ -43,16 +45,6 @@ typedef enum stream_status_event_t {
STREAM_EVENT_REMAP = 8
} stream_status_event_t;
/** Used to indicate the type of an OR connection event passed to the
* controller. The various types are defined in control-spec.txt */
typedef enum or_conn_status_event_t {
OR_CONN_EVENT_LAUNCHED = 0,
OR_CONN_EVENT_CONNECTED = 1,
OR_CONN_EVENT_FAILED = 2,
OR_CONN_EVENT_CLOSED = 3,
OR_CONN_EVENT_NEW = 4,
} or_conn_status_event_t;
/** Used to indicate the type of a buildtime event */
typedef enum buildtimeout_set_event_t {
BUILDTIMEOUT_SET_EVENT_COMPUTED = 0,
+1 -1
View File
@@ -90,7 +90,7 @@ connection_ext_or_transition(or_connection_t *conn)
conn->base_.type = CONN_TYPE_OR;
TO_CONN(conn)->state = 0; // set the state to a neutral value
control_event_or_conn_status(conn, OR_CONN_EVENT_NEW, 0);
connection_or_event_status(conn, OR_CONN_EVENT_NEW, 0);
connection_tls_start_handshake(conn, 1);
}