From c51761742d2cc133c4cd6b02671d693c7a7a8d4b Mon Sep 17 00:00:00 2001 From: GitTensor Miner Date: Wed, 21 Jan 2026 17:09:43 +0200 Subject: [PATCH] Improve RADIUS client error handling and socket cleanup - Enhanced error messages in util-server.js to preserve stack traces and provide better context with hostname and port information - Fixed potential race conditions in radius-client.js by adding proper socket cleanup and preventing multiple cleanup calls - Added socketClosed flag to prevent operations on closed sockets - Improved timeout handling to prevent memory leaks and ensure proper resource cleanup --- server/radius-client.js | 42 ++++++++++++++++++++++++++++++----------- server/util-server.js | 12 ++++++++++-- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/server/radius-client.js b/server/radius-client.js index d7c9a3150..207bc73f7 100644 --- a/server/radius-client.js +++ b/server/radius-client.js @@ -67,23 +67,47 @@ class RadiusClient { let attempts = 0; let responseReceived = false; let timeoutHandle; + let socketClosed = false; + + /** + * Safely close socket and clear timeout + * @returns {void} + */ + const cleanup = () => { + if (timeoutHandle) { + clearTimeout(timeoutHandle); + timeoutHandle = null; + } + if (!socketClosed) { + socketClosed = true; + try { + socket.close(); + } catch (err) { + // Ignore errors during cleanup + } + } + }; /** * Send RADIUS request with retry logic * @returns {void} */ const sendRequest = () => { + if (responseReceived || socketClosed) { + return; + } + attempts++; socket.send(encodedPacket, 0, encodedPacket.length, this.port, this.host, (err) => { if (err) { - socket.close(); + cleanup(); return reject(new Error(`Failed to send RADIUS request: ${err.message}`)); } // Set timeout for this attempt timeoutHandle = setTimeout(() => { - if (responseReceived) { + if (responseReceived || socketClosed) { return; } @@ -92,7 +116,7 @@ class RadiusClient { sendRequest(); } else { // All retries exhausted - socket.close(); + cleanup(); reject(new Error(`RADIUS request timeout after ${attempts} attempts`)); } }, this.timeout); @@ -101,23 +125,20 @@ class RadiusClient { // Handle response socket.on("message", (msg) => { - if (responseReceived) { + if (responseReceived || socketClosed) { return; } responseReceived = true; - clearTimeout(timeoutHandle); + cleanup(); let response; try { response = radius.decode({ packet: msg, secret: secret }); } catch (error) { - socket.close(); return reject(new Error(`RADIUS response decoding failed: ${error.message}`)); } - socket.close(); - // Map response code to match node-radius-client format const responseCode = response.code; @@ -140,10 +161,9 @@ class RadiusClient { // Handle socket errors socket.on("error", (err) => { - if (!responseReceived) { + if (!responseReceived && !socketClosed) { responseReceived = true; - clearTimeout(timeoutHandle); - socket.close(); + cleanup(); reject(new Error(`RADIUS socket error: ${err.message}`)); } }); diff --git a/server/util-server.js b/server/util-server.js index d7316dee3..ae0b64fcb 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -356,10 +356,18 @@ exports.radius = function ( ], }) .catch((error) => { + // Preserve error stack trace and provide better context if (error.response?.code) { - throw Error(error.response.code); + const radiusError = new Error(`RADIUS ${error.response.code} from ${hostname}:${port}`); + radiusError.response = error.response; + radiusError.originalError = error; + throw radiusError; } else { - throw Error(error.message); + // Preserve original error message and stack trace + const enhancedError = new Error(`RADIUS authentication failed for ${hostname}:${port}: ${error.message}`); + enhancedError.originalError = error; + enhancedError.stack = error.stack || enhancedError.stack; + throw enhancedError; } }); };