From 361912ec857a77dc92e7ac5cf313346300cd4091 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 23 Feb 2026 15:22:18 -0400 Subject: [PATCH] v1.2.17 - Add nip42_auth_timeout_sec (default 10s): close unauthenticated connections after timeout to prevent connection accumulation --- src/config.c | 9 +++++++++ src/default_config_event.h | 5 +++++ src/main.h | 4 ++-- src/websockets.c | 31 +++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/config.c b/src/config.c index f62dedb..0d03adb 100644 --- a/src/config.c +++ b/src/config.c @@ -952,6 +952,15 @@ static int validate_config_field(const char* key, const char* value, char* error return 0; } + // NIP-42 auth timeout + if (strcmp(key, "nip42_auth_timeout_sec") == 0) { + if (!is_valid_positive_integer(value) && strcmp(value, "0") != 0) { + snprintf(error_msg, error_size, "invalid nip42_auth_timeout_sec '%s' (must be non-negative integer)", value); + return -1; + } + return 0; + } + // SQLite performance tuning if (strcmp(key, "sqlite_mmap_size") == 0) { if (!is_valid_positive_integer(value) && strcmp(value, "0") != 0) { diff --git a/src/default_config_event.h b/src/default_config_event.h index 483ce1f..c9c812b 100644 --- a/src/default_config_event.h +++ b/src/default_config_event.h @@ -83,6 +83,11 @@ static const struct { // IP-based rate limiting or access control (which would require firewall protection anyway) {"trust_proxy_headers", "true"}, + // NIP-42 Authentication Timeout + // Seconds after connection before unauthenticated clients are disconnected (0 = disabled) + // Prevents unauthenticated connections from accumulating under heavy load + {"nip42_auth_timeout_sec", "10"}, + // SQLite Performance Tuning // mmap_size: bytes of database file to memory-map (0 = disabled, 268435456 = 256MB recommended) // Eliminates pread64 syscall overhead for database reads — significant CPU savings under load diff --git a/src/main.h b/src/main.h index 27d8d16..3d22985 100644 --- a/src/main.h +++ b/src/main.h @@ -13,8 +13,8 @@ // Using CRELAY_ prefix to avoid conflicts with nostr_core_lib VERSION macros #define CRELAY_VERSION_MAJOR 1 #define CRELAY_VERSION_MINOR 2 -#define CRELAY_VERSION_PATCH 16 -#define CRELAY_VERSION "v1.2.16" +#define CRELAY_VERSION_PATCH 17 +#define CRELAY_VERSION "v1.2.17" // Relay metadata (authoritative source for NIP-11 information) #define RELAY_NAME "C-Relay" diff --git a/src/websockets.c b/src/websockets.c index ce2699d..d9747c1 100644 --- a/src/websockets.c +++ b/src/websockets.c @@ -1011,6 +1011,21 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso } else { send_notice_message(wsi, pss, "NIP-42 authentication required for subscriptions"); DEBUG_WARN("REQ rejected: NIP-42 authentication required"); + + // Auth timeout: close connection if challenge was sent but client + // hasn't authenticated within nip42_auth_timeout_sec seconds + int auth_timeout = get_config_int("nip42_auth_timeout_sec", 10); + if (auth_timeout > 0 && pss->connection_established > 0) { + time_t connection_age = time(NULL) - pss->connection_established; + if (connection_age >= auth_timeout) { + DEBUG_LOG("Closing unauthenticated connection from %s after %ld seconds (timeout=%d)", + pss->client_ip, connection_age, auth_timeout); + lws_close_reason(wsi, LWS_CLOSE_STATUS_POLICY_VIOLATION, + (unsigned char*)"Authentication timeout", 22); + cJSON_Delete(json); + return -1; + } + } } cJSON_Delete(json); // Note: complete_message points to reassembly_buffer, which is managed separately @@ -1768,6 +1783,22 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso } else { send_notice_message(wsi, pss, "NIP-42 authentication required for subscriptions"); DEBUG_WARN("REQ rejected: NIP-42 authentication required"); + + // Auth timeout: close connection if challenge was sent but client + // hasn't authenticated within nip42_auth_timeout_sec seconds + int auth_timeout = get_config_int("nip42_auth_timeout_sec", 10); + if (auth_timeout > 0 && pss->connection_established > 0) { + time_t connection_age = time(NULL) - pss->connection_established; + if (connection_age >= auth_timeout) { + DEBUG_LOG("Closing unauthenticated connection from %s after %ld seconds (timeout=%d)", + pss->client_ip, connection_age, auth_timeout); + lws_close_reason(wsi, LWS_CLOSE_STATUS_POLICY_VIOLATION, + (unsigned char*)"Authentication timeout", 22); + cJSON_Delete(json); + free(message); + return -1; + } + } } cJSON_Delete(json); free(message);