[tor-commits] [tor/master] Add ORCONN event pubsub system

nickm at torproject.org nickm at torproject.org
Fri Dec 21 19:28:10 UTC 2018


commit 271b50f54abac7af44e3e54589ff965d3cdac816
Author: Taylor Yu <catalyst at torproject.org>
Date:   Sun Dec 16 16:05:58 2018 -0600

    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.
---
 src/app/main/subsystem_list.c  |   2 +
 src/core/include.am            |   3 ++
 src/core/mainloop/connection.c |   2 +-
 src/core/or/connection_or.c    |  73 +++++++++++++++++++++----
 src/core/or/connection_or.h    |  30 ++---------
 src/core/or/orconn_event.c     |  81 ++++++++++++++++++++++++++++
 src/core/or/orconn_event.h     | 120 +++++++++++++++++++++++++++++++++++++++++
 src/core/or/orconn_event_sys.h |  12 +++++
 src/feature/control/control.h  |  12 +----
 src/feature/relay/ext_orport.c |   2 +-
 10 files changed, 288 insertions(+), 49 deletions(-)

diff --git a/src/app/main/subsystem_list.c b/src/app/main/subsystem_list.c
index 8640329e9..185873809 100644
--- a/src/app/main/subsystem_list.c
+++ b/src/app/main/subsystem_list.c
@@ -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);
diff --git a/src/core/include.am b/src/core/include.am
index 3cd6e83ed..a1fae7360 100644
--- a/src/core/include.am
+++ b/src/core/include.am
@@ -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		\
diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c
index 601c872c9..6cc612270 100644
--- a/src/core/mainloop/connection.c
+++ b/src/core/mainloop/connection.c
@@ -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);
diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c
index 04ab4fe4a..a12ea55c4 100644
--- a/src/core/or/connection_or.c
+++ b/src/core/or/connection_or.c
@@ -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. */
diff --git a/src/core/or/connection_or.h b/src/core/or/connection_or.h
index d4bcdd93e..5f4856d51 100644
--- a/src/core/or/connection_or.h
+++ b/src/core/or/connection_or.h
@@ -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);
diff --git a/src/core/or/orconn_event.c b/src/core/or/orconn_event.c
new file mode 100644
index 000000000..11f5ed966
--- /dev/null
+++ b/src/core/or/orconn_event.c
@@ -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,
+};
diff --git a/src/core/or/orconn_event.h b/src/core/or/orconn_event.h
new file mode 100644
index 000000000..4c999e53b
--- /dev/null
+++ b/src/core/or/orconn_event.h
@@ -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) */
diff --git a/src/core/or/orconn_event_sys.h b/src/core/or/orconn_event_sys.h
new file mode 100644
index 000000000..763902338
--- /dev/null
+++ b/src/core/or/orconn_event_sys.h
@@ -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) */
diff --git a/src/feature/control/control.h b/src/feature/control/control.h
index d78ce4d87..d3245fcaf 100644
--- a/src/feature/control/control.h
+++ b/src/feature/control/control.h
@@ -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,
diff --git a/src/feature/relay/ext_orport.c b/src/feature/relay/ext_orport.c
index 3607bdede..0a649f274 100644
--- a/src/feature/relay/ext_orport.c
+++ b/src/feature/relay/ext_orport.c
@@ -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);
 }
 





More information about the tor-commits mailing list