From: Vsevolod Stakhov Date: Sun, 8 Feb 2026 19:25:44 +0000 (+0000) Subject: [Feature] Add SSL server-side accept support X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d04b367dbd3f71fc1865e119e963c5f099f2aed4;p=thirdparty%2Frspamd.git [Feature] Add SSL server-side accept support Add rspamd_ssl_accept_fd() function for server-side SSL handshakes: - Mirrors existing rspamd_ssl_connect_fd() but for accepting connections - Adds ssl_conn_init_accept state for server-side SSL state machine - Handles SSL_accept() with proper WANT_READ/WANT_WRITE event handling This enables SSL-capable server implementations (e.g. SMTP proxy with STARTTLS). --- diff --git a/src/libserver/ssl_util.c b/src/libserver/ssl_util.c index c10a869dd8..f41d310579 100644 --- a/src/libserver/ssl_util.c +++ b/src/libserver/ssl_util.c @@ -38,7 +38,8 @@ enum rspamd_ssl_state { ssl_conn_reset = 0, - ssl_conn_init, + ssl_conn_init, /* Client-side: connecting */ + ssl_conn_init_accept, /* Server-side: accepting */ ssl_conn_connected, ssl_next_read, ssl_next_write, @@ -570,7 +571,7 @@ rspamd_ssl_event_handler(int fd, short what, gpointer ud) switch (conn->state) { case ssl_conn_init: - /* Continue connection */ + /* Continue client-side connection */ ret = SSL_connect(conn->ssl); if (ret == 1) { @@ -607,6 +608,38 @@ rspamd_ssl_event_handler(int fd, short what, gpointer ud) rspamd_ev_watcher_reschedule(conn->event_loop, conn->ev, what); } break; + case ssl_conn_init_accept: + /* Continue server-side accept handshake */ + ret = SSL_accept(conn->ssl); + + if (ret == 1) { + rspamd_ev_watcher_stop(conn->event_loop, conn->ev); + msg_debug_ssl("ssl accept: connected"); + conn->state = ssl_conn_connected; + conn->handler(fd, EV_WRITE, conn->handler_data); + } + else { + ret = SSL_get_error(conn->ssl, ret); + + if (ret == SSL_ERROR_WANT_READ) { + msg_debug_ssl("ssl accept: need read"); + what = EV_READ; + } + else if (ret == SSL_ERROR_WANT_WRITE) { + msg_debug_ssl("ssl accept: need write"); + what = EV_WRITE; + } + else { + rspamd_ev_watcher_stop(conn->event_loop, conn->ev); + rspamd_tls_set_error(ret, "accept", &err); + conn->err_handler(conn->handler_data, err); + g_error_free(err); + return; + } + + rspamd_ev_watcher_reschedule(conn->event_loop, conn->ev, what); + } + break; case ssl_next_read: rspamd_ev_watcher_reschedule(conn->event_loop, conn->ev, EV_READ); conn->state = ssl_conn_connected; @@ -760,6 +793,87 @@ rspamd_ssl_connect_fd(struct rspamd_ssl_connection *conn, int fd, return TRUE; } +gboolean +rspamd_ssl_accept_fd(struct rspamd_ssl_connection *conn, int fd, + struct rspamd_io_ev *ev, ev_tstamp timeout, + rspamd_ssl_handler_t handler, rspamd_ssl_error_handler_t err_handler, + gpointer handler_data) +{ + int ret; + + g_assert(conn != NULL); + + /* Ensure that we start from the empty SSL errors stack */ + ERR_clear_error(); + conn->ssl = SSL_new(conn->ssl_ctx->s); + + SSL_set_app_data(conn->ssl, conn); + msg_debug_ssl("new ssl accept connection %p", conn->ssl); + + if (conn->state != ssl_conn_reset) { + return FALSE; + } + + /* We dup fd to allow graceful closing */ + int nfd = dup(fd); + + if (nfd == -1) { + return FALSE; + } + + conn->fd = nfd; + conn->ev = ev; + conn->handler = handler; + conn->err_handler = err_handler; + conn->handler_data = handler_data; + conn->verify_peer = FALSE; /* Server-side doesn't verify client by default */ + + if (SSL_set_fd(conn->ssl, conn->fd) != 1) { + close(conn->fd); + return FALSE; + } + + conn->state = ssl_conn_init_accept; + + ret = SSL_accept(conn->ssl); + + if (ret == 1) { + conn->state = ssl_conn_connected; + + msg_debug_ssl("ssl accept: connected immediately, start write event"); + rspamd_ev_watcher_stop(conn->event_loop, ev); + rspamd_ev_watcher_init(ev, nfd, EV_WRITE, rspamd_ssl_event_handler, conn); + rspamd_ev_watcher_start(conn->event_loop, ev, timeout); + } + else { + ret = SSL_get_error(conn->ssl, ret); + + if (ret == SSL_ERROR_WANT_READ) { + msg_debug_ssl("ssl accept: not connected, want read"); + } + else if (ret == SSL_ERROR_WANT_WRITE) { + msg_debug_ssl("ssl accept: not connected, want write"); + } + else { + GError *err = NULL; + + conn->shut = ssl_shut_unclean; + rspamd_tls_set_error(ret, "initial accept", &err); + msg_debug_ssl("ssl accept: not connected, fatal error %e", err); + g_error_free(err); + + return FALSE; + } + + rspamd_ev_watcher_stop(conn->event_loop, ev); + rspamd_ev_watcher_init(ev, nfd, EV_WRITE | EV_READ, + rspamd_ssl_event_handler, conn); + rspamd_ev_watcher_start(conn->event_loop, ev, timeout); + } + + return TRUE; +} + void rspamd_ssl_connection_restore_handlers(struct rspamd_ssl_connection *conn, rspamd_ssl_handler_t handler, rspamd_ssl_error_handler_t err_handler, diff --git a/src/libserver/ssl_util.h b/src/libserver/ssl_util.h index 628f2aed6b..802ba96865 100644 --- a/src/libserver/ssl_util.h +++ b/src/libserver/ssl_util.h @@ -56,6 +56,22 @@ gboolean rspamd_ssl_connect_fd(struct rspamd_ssl_connection *conn, int fd, rspamd_ssl_handler_t handler, rspamd_ssl_error_handler_t err_handler, gpointer handler_data); +/** + * Accepts SSL session on an already connected (accepted) FD + * @param conn connection + * @param fd fd to use (already accepted TCP socket) + * @param ev event to use + * @param tv timeout for handshake + * @param handler connected session handler + * @param err_handler error handler + * @param handler_data opaque data + * @return TRUE if handshake has started + */ +gboolean rspamd_ssl_accept_fd(struct rspamd_ssl_connection *conn, int fd, + struct rspamd_io_ev *ev, ev_tstamp timeout, + rspamd_ssl_handler_t handler, rspamd_ssl_error_handler_t err_handler, + gpointer handler_data); + /** * Restores SSL handlers for the existing ssl connection (e.g. after keepalive) * @param conn