]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Fix] Properly populate timeout read in tcp_lua.c
authorDmitriy Alekseev <1865999+dragoangel@users.noreply.github.com>
Fri, 22 May 2026 11:44:43 +0000 (13:44 +0200)
committerDmitriy Alekseev <1865999+dragoangel@users.noreply.github.com>
Fri, 22 May 2026 11:44:43 +0000 (13:44 +0200)
src/lua/lua_tcp.c

index fe7e3f145287696d0613e5abbb006e5e95fb9e99..27b21d147586f68c6f1e2679b0ea2702b7b23bb0 100644 (file)
@@ -846,16 +846,18 @@ lua_tcp_connect_helper(struct lua_tcp_cbdata *cbd)
 
        if (cbd->thread == NULL) {
                /*
-                * Async pure-connect probe: the LUA_WANT_CONNECT marker was queued only
-                * to keep plan_handler_event from finishing the session before the
-                * socket completes the dial. The on_connect callback (if any) was
-                * already fired in lua_tcp_handler when LUA_TCP_FLAG_CONNECTED got
-                * set; shift the marker and re-plan so the now-empty queue triggers
-                * the FINISHED tear-down.
+                * Async path: the LUA_WANT_CONNECT marker was queued either as a
+                * pure-connect probe (queue empty after shift -> FINISHED tear-down)
+                * or ahead of a LUA_WANT_READ for connect-phase detection on a
+                * read-without-write request (queue head is now the read handler).
+                * The on_connect callback (if any) was already fired in
+                * lua_tcp_handler when LUA_TCP_FLAG_CONNECTED got set; shift the
+                * marker and re-plan with can_read/can_write enabled so any remaining
+                * handler gets armed correctly.
                 */
                msg_debug_tcp("tcp connected (async probe)");
                lua_tcp_shift_handler(cbd);
-               lua_tcp_plan_handler_event(cbd, FALSE, FALSE);
+               lua_tcp_plan_handler_event(cbd, TRUE, TRUE);
                return;
        }
 
@@ -2023,19 +2025,40 @@ lua_tcp_request(lua_State *L)
        }
 
        /*
-        * Pure-probe shape: caller wants to verify TCP connectivity (and/or be
-        * notified of connect-phase errors via on_error) without queueing any
-        * read or write. Push a LUA_WANT_CONNECT marker so plan_handler_event
-        * arms the EV_WRITE watcher; the marker is shifted in
-        * lua_tcp_connect_helper once the connect resolves. Without this,
-        * plan_handler_event would see an empty queue and tear the session down
-        * before the dial ever completes.
+        * Two shapes need an explicit LUA_WANT_CONNECT marker so that EV_WRITE
+        * is armed for the dial and LUA_TCP_FLAG_CONNECTED is set in the proper
+        * connect-phase path:
+        *
+        *   1. Pure probe (empty queue, only on_connect/on_error registered):
+        *      without the marker plan_handler_event would tear the session down
+        *      before the dial completes.
+        *
+        *   2. Read-without-prior-write (queue head is LUA_WANT_READ): without
+        *      the marker plan_handler_event arms EV_READ with read_timeout
+        *      straight away, the socket-writable connect signal is never
+        *      consumed, LUA_TCP_FLAG_CONNECTED stays unset, and any pre-byte
+        *      error/timeout misroutes to on_error (looking like a connect
+        *      failure) -- plus FINISHED|CONNECTED gates on conn:close() leak
+        *      refcounts. The natural connect detection that LUA_WANT_WRITE
+        *      gets for free (writes require socket-writable) is missing here.
+        *
+        * In both shapes the marker is shifted in lua_tcp_connect_helper once
+        * the connect resolves.
         */
-       if (g_queue_get_length(cbd->handlers) == 0 &&
-               (conn_cbref != -1 || error_cbref != -1)) {
-               struct lua_tcp_handler *ch = g_malloc0(sizeof(*ch));
-               ch->type = LUA_WANT_CONNECT;
-               g_queue_push_tail(cbd->handlers, ch);
+       if (g_queue_get_length(cbd->handlers) == 0) {
+               if (conn_cbref != -1 || error_cbref != -1) {
+                       struct lua_tcp_handler *ch = g_malloc0(sizeof(*ch));
+                       ch->type = LUA_WANT_CONNECT;
+                       g_queue_push_tail(cbd->handlers, ch);
+               }
+       }
+       else {
+               struct lua_tcp_handler *first = g_queue_peek_head(cbd->handlers);
+               if (first != NULL && first->type == LUA_WANT_READ) {
+                       struct lua_tcp_handler *ch = g_malloc0(sizeof(*ch));
+                       ch->type = LUA_WANT_CONNECT;
+                       g_queue_push_head(cbd->handlers, ch);
+               }
        }
 
        cbd->connect_cb = conn_cbref;