From 1c5bd790ea9ddba0e3691243392cd0c77f9e363e Mon Sep 17 00:00:00 2001 From: m1ngsama Date: Mon, 8 Dec 2025 10:49:47 +0800 Subject: [PATCH] Fix SSH connection stability issues Addresses the "Key exchange failed: Socket error: disconnected" errors that were causing service instability. Changes: - Add 10-second timeout for SSH network operations to prevent hung connections - Enable TCP keepalive with aggressive parameters (60s idle, 10s interval, 3 probes) - Disable SSH compression to reduce CPU overhead - Set log verbosity to NOLOG to eliminate excessive logging that was filling up systemd journal and potentially impacting performance - Remove error logging for failed key exchanges to reduce log spam (these are normal when clients disconnect during handshake) The timeout and keepalive settings ensure dead connections are detected and cleaned up quickly, while reduced logging minimizes I/O overhead during high connection churn scenarios. Tested: Server builds cleanly and starts successfully on macOS. TCP keepalive is Linux-specific and properly guarded with #ifdef. --- src/ssh_server.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/ssh_server.c b/src/ssh_server.c index 4023611..7a291a7 100644 --- a/src/ssh_server.c +++ b/src/ssh_server.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -649,8 +650,8 @@ int ssh_server_init(int port) { ssh_bind_options_set(g_sshbind, SSH_BIND_OPTIONS_BINDPORT, &port); ssh_bind_options_set(g_sshbind, SSH_BIND_OPTIONS_BINDADDR, "0.0.0.0"); - /* Set verbose level for debugging */ - int verbosity = SSH_LOG_WARNING; + /* Set verbose level - use NOLOG to reduce overhead and log spam */ + int verbosity = SSH_LOG_NOLOG; ssh_bind_options_set(g_sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &verbosity); if (ssh_bind_listen(g_sshbind) < 0) { @@ -676,6 +677,14 @@ int ssh_server_start(int unused) { continue; } + /* Configure session timeouts and options */ + long timeout = 10; /* 10 second timeout for network operations */ + ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &timeout); + + /* Disable compression to reduce CPU overhead */ + int no_compression = 0; + ssh_options_set(session, SSH_OPTIONS_COMPRESSION, &no_compression); + /* Accept connection */ if (ssh_bind_accept(g_sshbind, session) != SSH_OK) { fprintf(stderr, "Error accepting connection: %s\n", ssh_get_error(g_sshbind)); @@ -683,9 +692,26 @@ int ssh_server_start(int unused) { continue; } - /* Perform key exchange */ + /* Enable TCP keepalive on the accepted socket */ + int sock = ssh_get_fd(session); + if (sock >= 0) { + int optval = 1; + setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)); + + /* Set keepalive parameters (Linux-specific) */ + #ifdef __linux__ + int keepidle = 60; /* Start probes after 60 seconds of idle */ + int keepintvl = 10; /* Send probe every 10 seconds */ + int keepcnt = 3; /* Drop after 3 failed probes */ + setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle)); + setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl, sizeof(keepintvl)); + setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, sizeof(keepcnt)); + #endif + } + + /* Perform key exchange with timeout protection */ if (ssh_handle_key_exchange(session) != SSH_OK) { - fprintf(stderr, "Key exchange failed: %s\n", ssh_get_error(session)); + /* Don't log every failed key exchange to reduce spam */ ssh_disconnect(session); ssh_free(session); continue;