]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Check after each pre/post callback to see if we should free the connection
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Mon, 18 Nov 2019 14:00:38 +0000 (09:00 -0500)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Mon, 18 Nov 2019 14:04:29 +0000 (09:04 -0500)
src/lib/server/connection.c
src/lib/server/connection.h

index 443dc1c6c99e6a8acac5663261370b81bac23811..5208c3815890e188feb8c6fa12b95923d8fa43a7 100644 (file)
@@ -125,7 +125,8 @@ do { \
 static inline void connection_watch_call(fr_connection_t *conn, fr_dlist_head_t *list)
 {
        fr_connection_watch_entry_t *entry = NULL;
-       if (fr_dlist_empty(list)) return;
+
+       if (conn->deferred_free) return;        /* If something freed the connection then don't call more watchers */
 
        while ((entry = fr_dlist_next(list, entry))) {
                entry->func(conn, conn->state, entry->uctx);
@@ -140,12 +141,24 @@ static inline void connection_watch_call(fr_connection_t *conn, fr_dlist_head_t
 /** Call the pre handler watch functions
  *
  */
-#define WATCH_PRE(_conn) connection_watch_call((_conn), &(_conn)->watch_pre[(_conn)->state]);
+#define WATCH_PRE(_conn) \
+do { \
+       if (fr_dlist_empty(&(_conn)->watch_pre[(_conn)->state])) break; \
+       HANDLER_BEGIN(conn); \
+       connection_watch_call((_conn), &(_conn)->watch_pre[(_conn)->state]); \
+       HANDLER_END(conn); \
+} while(0)
 
 /** Call the post handler watch functions
  *
  */
-#define WATCH_POST(_conn) connection_watch_call((_conn), &(_conn)->watch_post[(_conn)->state]);
+#define WATCH_POST(_conn) \
+do { \
+       if (fr_dlist_empty(&(_conn)->watch_post[(_conn)->state])) break; \
+       HANDLER_BEGIN(conn); \
+       connection_watch_call((_conn), &(_conn)->watch_post[(_conn)->state]); \
+       HANDLER_END(conn); \
+} while(0)
 
 /*
  *     State transition functions
@@ -436,12 +449,16 @@ static void connection_state_failed_enter(fr_connection_t *conn, fr_time_t now)
         *      API client can free any resources associated
         *      with the connection handle.
         */
-       HANDLER_BEGIN(conn);
        WATCH_PRE(conn);
-       if (conn->close && !conn->is_closed) conn->close(conn->h, conn->uctx);
-       conn->is_closed = true;         /* Ensure close doesn't get called twice if the connection is freed */
+       if (conn->close && !conn->is_closed) {
+               HANDLER_BEGIN(conn);
+               conn->close(conn->h, conn->uctx);
+               conn->is_closed = true;         /* Ensure close doesn't get called twice if the connection is freed */
+               HANDLER_END(conn);
+       } else {
+               conn->is_closed = true;
+       }
        WATCH_POST(conn);
-       HANDLER_END(conn);
 
        /*
         *      If there's a failed callback, give it the
@@ -536,15 +553,16 @@ static void connection_state_connected_enter(fr_connection_t *conn, UNUSED fr_ti
 
        fr_event_timer_delete(conn->el, &conn->connection_timer);
 
-       HANDLER_BEGIN(conn);
        WATCH_PRE(conn);
        if (conn->open) {
+               HANDLER_BEGIN(conn);
                ret = conn->open(conn->el, conn->h, conn->uctx);
+               HANDLER_END(conn);
        } else {
                ret = FR_CONNECTION_STATE_CONNECTED;
        }
        WATCH_POST(conn);
-       HANDLER_END(conn);
+
 
        switch (ret) {
        /*
@@ -638,15 +656,16 @@ static void connection_state_init_enter(fr_connection_t *conn, fr_time_t now)
        /*
         *      If we have an init callback, call it.
         */
-       HANDLER_BEGIN(conn);
+
        WATCH_PRE(conn);
        if (conn->init) {
+               HANDLER_BEGIN(conn);
                ret = conn->init(&conn->h, conn, conn->uctx);
+               HANDLER_END(conn);
        } else {
                ret = FR_CONNECTION_STATE_CONNECTING;
        }
        WATCH_POST(conn);
-       HANDLER_END(conn);
 
        switch (ret) {
        case FR_CONNECTION_STATE_CONNECTING:
index c35c842b10d702ea03c9952997338ae3130f862c..90d3980311e34c0f05617ca9c2833f0a688b8f70 100644 (file)
@@ -47,8 +47,6 @@ typedef enum {
        FR_CONNECTION_STATE_MAX
 } fr_connection_state_t;
 
-typedef void(*fr_connection_watch_t)(fr_connection_t *conn, fr_connection_state_t state, void *uctx);
-
 extern fr_table_num_sorted_t const fr_connection_states[];
 extern size_t fr_connection_states_len;
 
@@ -108,11 +106,22 @@ typedef fr_connection_state_t (*fr_connection_failed_t)(void *h, fr_connection_s
  * If this callback does not close the file descriptor, the server will leak
  * file descriptors.
  *
- * @param[in] h        to close.
+ * @param[in] h                Handle to close.
  * @param[in] uctx     User context.
  */
 typedef void (*fr_connection_close_t)(void *h, void *uctx);
 
+/** Receive a notification when a connection enters a particular state
+ *
+ * It is permitted for watchers to signal state changes, and/or to free the
+ * connection.  The actual free will be deferred until the watcher returns.
+ *
+ * @param[in] conn     Being watched.
+ * @param[in] state    That was entered.
+ * @param[in] uctx     that was passed to fr_connection_add_watch_*.
+ */
+typedef void(*fr_connection_watch_t)(fr_connection_t *conn, fr_connection_state_t state, void *uctx);
+
 fr_event_list_t                *fr_connection_get_el(fr_connection_t const *conn);
 
 void                   *fr_connection_get_handle(fr_connection_t const *conn);