From f17e845c1d952adcf2f5a66d7ebcc48fa8337473 Mon Sep 17 00:00:00 2001 From: KubaPro010 Date: Sat, 21 Mar 2026 15:18:46 +0100 Subject: [PATCH] some changes --- package-lock.json | 12 ++---------- server/chat.js | 2 +- server/console.js | 4 +++- server/datahandler.js | 2 +- server/endpoints.js | 6 +++--- server/helpers.js | 16 +++++++++++++++- server/index.js | 6 +++--- server/rds.js | 16 ++++++++++++---- server/rds_country.js | 4 ++-- server/server_config.js | 3 ++- server/stream/ws.js | 3 ++- server/tx_search.js | 10 ++++++---- web/403.ejs | 2 +- web/index.ejs | 2 +- 14 files changed, 54 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index 12c7186..03ea968 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "fm-dx-webserver", - "version": "1.4.0", + "version": "1.4.0a", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "fm-dx-webserver", - "version": "1.4.0", + "version": "1.4.0a", "license": "ISC", "dependencies": { "@mapbox/node-pre-gyp": "2.0.3", @@ -17,7 +17,6 @@ "ffmpeg-static": "5.3.0", "figlet": "^1.10.0", "http": "0.0.1-security", - "koffi": "2.7.2", "net": "1.0.2", "serialport": "13.0.0", "ws": "8.19.0" @@ -1168,13 +1167,6 @@ "node": ">=10" } }, - "node_modules/koffi": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/koffi/-/koffi-2.7.2.tgz", - "integrity": "sha512-AWcsEKETQuELxK0Wq/aXDkDiNFFY41TxZQSrKm2Nd6HO/KTHeohPOOIlh2OfQnBXJbRjx5etpWt8cbqMUZo2sg==", - "hasInstallScript": true, - "license": "MIT" - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", diff --git a/server/chat.js b/server/chat.js index f46655f..2ae5b39 100644 --- a/server/chat.js +++ b/server/chat.js @@ -16,7 +16,7 @@ function createChatServer(storage) { ws.isAlive = true; ws.on('pong', heartbeat); - const clientIp = request.headers['x-forwarded-for'] || request.socket.remoteAddress; + const clientIp = helpers.getIpAddress(request); const userCommandHistory = {}; if (serverConfig.webserver.banlist?.includes(clientIp)) { diff --git a/server/console.js b/server/console.js index 0e17a08..dff1dce 100644 --- a/server/console.js +++ b/server/console.js @@ -21,6 +21,7 @@ const MESSAGE_PREFIX = { FFMPEG: "\x1b[36m[FFMPEG]\x1b[0m", INFO: "\x1b[32m[INFO]\x1b[0m", WARN: "\x1b[33m[WARN]\x1b[0m", + SECURITY: "\x1b[36m[SECURITY]\x1b[0m", }; const getCurrentTime = () => { @@ -44,6 +45,7 @@ const logMessage = (type, messages) => { if(type !== 'FFMPEG') appendLogToBuffer(logMessage); }; +const logSecurity = (...messages) => logMessage('SECURITY', messages); const logDebug = (...messages) => logMessage('DEBUG', messages); const logChat = (message) => logMessage('CHAT', [`${message.nickname} (${message.ip}) sent a chat message: ${message.message}`]); const logError = (...messages) => logMessage('ERROR', messages); @@ -87,4 +89,4 @@ process.on('exit', flushLogBuffer); process.on('SIGINT', gracefulExit); process.on('SIGTERM', gracefulExit); -module.exports = { logError, logDebug, logFfmpeg, logInfo, logWarn, logs, logChat }; \ No newline at end of file +module.exports = { logError, logDebug, logFfmpeg, logInfo, logWarn, logs, logChat, logSecurity }; \ No newline at end of file diff --git a/server/datahandler.js b/server/datahandler.js index dfa03fd..bed09ad 100644 --- a/server/datahandler.js +++ b/server/datahandler.js @@ -106,8 +106,8 @@ function handleData(wss, receivedData, rdsWss) { case receivedLine.startsWith('T'): // Frequency modifiedData = receivedLine.substring(1).split(",")[0]; - rdsReset(); if((modifiedData / 1000).toFixed(3) == dataToSend.freq) return; // Prevent tune spamming using scrollwheel + rdsReset(); parsedValue = parseFloat(modifiedData); diff --git a/server/endpoints.js b/server/endpoints.js index 4748904..bb3fbfd 100644 --- a/server/endpoints.js +++ b/server/endpoints.js @@ -19,10 +19,10 @@ const allPluginConfigs = require('./plugins'); // Endpoints router.get('/', (req, res) => { - let requestIp = req.headers['x-forwarded-for'] || req.socket.remoteAddress; + let requestIp = helpers.getIpAddress(req); const normalizedIp = requestIp?.replace(/^::ffff:/, ''); - const ipList = (normalizedIp || '').split(',').map(ip => ip.trim()).filter(Boolean); // in case there are multiple IPs (proxy), we need to check all of them + const ipList = (normalizedIp || '').split(',').map(ip => ip.trim()).filter(Boolean); // in case there are multiple IPs (proxy), we need to check all of them (No we don't) const banEntry = serverConfig.webserver.banlist.find(banEntry => ipList.includes(banEntry[0])); @@ -385,7 +385,7 @@ router.get('/log_fmlist', (req, res) => { return; } - const clientIp = req.headers['x-forwarded-for'] || req.connection.remoteAddress; + const clientIp = helpers.getIpAddress(req); const txId = dataHandler.dataToSend.txInfo.id; // Extract the ID if (!canLog(txId)) { diff --git a/server/helpers.js b/server/helpers.js index f1bd5f4..caab540 100644 --- a/server/helpers.js +++ b/server/helpers.js @@ -343,10 +343,24 @@ function findServerFiles(plugins) { return results; } +function getIpAddress(request) { + const remoteIp = request.socket.remoteAddress; + const xff = request.headers['x-forwarded-for']; + + // If X-Forwarded-For is present but request is NOT from a trusted proxy → suspicious + if (xff && !serverConfig.trustedProxies.includes(remoteIp)) { + consoleCmd.logSecurity(`Untrusted proxy tried to set X-Forwarded-For: ${xff} (remote: ${remoteIp})`); + return remoteIp; + } + + if (xff && serverConfig.trustedProxies.includes(remoteIp)) return xff.split(',')[0].trim(); + return remoteIp; +} + module.exports = { authenticateWithXdrd, parseMarkdown, handleConnect, removeMarkdown, formatUptime, resolveDataBuffer, kickClient, checkIPv6Support, checkLatency, antispamProtection, escapeHtml, findServerFiles, - startPluginsWithDelay + startPluginsWithDelay, getIpAddress } \ No newline at end of file diff --git a/server/index.js b/server/index.js index aa667c4..fa847ca 100644 --- a/server/index.js +++ b/server/index.js @@ -301,7 +301,7 @@ setInterval(() => { wss.on('connection', (ws, request) => { const output = serverConfig.xdrd.wirelessConnection ? client : serialport; - let clientIp = request.headers['x-forwarded-for'] || request.socket.remoteAddress; + let clientIp = helpers.getIpAddress(request); const userCommandHistory = {}; const normalizedClientIp = clientIp?.replace(/^::ffff:/, ''); @@ -486,7 +486,7 @@ wss.on('connection', (ws, request) => { // Additional web socket for using plugins pluginsWss.on('connection', (ws, request) => { - const clientIp = request.headers['x-forwarded-for'] || request.socket.remoteAddress; + const clientIp = helpers.getIpAddress(request); const userCommandHistory = {}; if (serverConfig.webserver.banlist?.includes(clientIp)) { ws.close(1008, 'Banned IP'); @@ -524,7 +524,7 @@ pluginsWss.on('connection', (ws, request) => { // Websocket register for /text, /audio and /chat paths httpServer.on('upgrade', (request, socket, head) => { - const clientIp = request.headers['x-forwarded-for'] || request.socket.remoteAddress; + const clientIp = helpers.getIpAddress(request); if (serverConfig.webserver.banlist?.includes(clientIp)) { socket.destroy(); return; diff --git a/server/rds.js b/server/rds.js index 2aa318e..c165ecc 100644 --- a/server/rds.js +++ b/server/rds.js @@ -232,12 +232,20 @@ class RDSDecoder { if(d_error > 2) return; // Don't risk it + const idx = blockB & 0x3; + + const last_err = this.ps_errors[idx * 2]; + const cur_err = Math.ceil(d_error * (10/3)); + const character_a = decode_charset(blockD >> 8); + const character_b = decode_charset(blockD & 0xFF); - this.ps[idx * 2] = decode_charset(blockD >> 8); - this.ps[idx * 2 + 1] = decode_charset(blockD & 0xFF); - this.ps_errors[idx * 2] = Math.ceil(d_error * (10/3)); - this.ps_errors[idx * 2 + 1] = Math.ceil(d_error * (10/3)); + if(last_err < cur_err && this.ps[idx * 2] == character_a && this.ps[idx * 2 + 1] == character_b) return; + + this.ps[idx * 2] = character_a; + this.ps[idx * 2 + 1] = character_b; + this.ps_errors[idx * 2] = cur_err; + this.ps_errors[idx * 2 + 1] = cur_err; this.data.ps = this.ps.join(''); this.data.ps_errors = this.ps_errors.join(','); diff --git a/server/rds_country.js b/server/rds_country.js index 915fd12..a93077c 100644 --- a/server/rds_country.js +++ b/server/rds_country.js @@ -105,7 +105,7 @@ var countries = [ "Turkey", "Palestine", "Turkmenistan", - "Poland", + "Rzeczpospolita Polska", "Uganda", "Portugal", "Ukraine", @@ -539,7 +539,7 @@ const rdsEccE0E5Lut = [ ], // E2 [ - "Morocco", "Czechia", "Poland", "Vatican", "Slovakia", + "Morocco", "Czechia", "Rzeczpospolita Polska", "Vatican", "Slovakia", "Syria", "Tunisia", "", "Liechtenstein", "Iceland", "Monaco", "Lithuania", "Serbia", "Spain", "Norway" ], diff --git a/server/server_config.js b/server/server_config.js index e1a065a..40ab241 100644 --- a/server/server_config.js +++ b/server/server_config.js @@ -126,7 +126,8 @@ let serverConfig = { stereoNoUsers: "0", antennaStartup: "0", antennaNoUsers: "0", - antennaNoUsersDelay: false + antennaNoUsersDelay: false, + trustedProxies: [] }; // Function to add missing fields without overwriting existing values diff --git a/server/stream/ws.js b/server/stream/ws.js index 12ec920..d68a156 100644 --- a/server/stream/ws.js +++ b/server/stream/ws.js @@ -1,11 +1,12 @@ const WebSocket = require('ws'); const { serverConfig } = require('../server_config'); const audio_pipe = require('./index.js'); +const { getIpAddress } = require("../helpers.js") const audioWss = new WebSocket.Server({ noServer: true, skipUTF8Validation: true }); audioWss.on('connection', (ws, request) => { - const clientIp = request.headers['x-forwarded-for'] || request.socket.remoteAddress; + const clientIp = getIpAddress(request); if (serverConfig.webserver.banlist?.includes(clientIp)) { ws.close(1008, 'Banned IP'); diff --git a/server/tx_search.js b/server/tx_search.js index 7a8bdfe..71214de 100644 --- a/server/tx_search.js +++ b/server/tx_search.js @@ -26,7 +26,6 @@ const WebSocket = require('ws'); // Get weighting values based on algorithm setting. // Defaults = algorithm 1 let weightedErp = 10; -let weightedDist = 400; const algorithms = [ [10, 400], [30, 500], @@ -268,7 +267,11 @@ async function fetchTx(freq, piCode, rdsPs) { if (filteredLocations.length > 1) { const extraFilteredLocations = filteredLocations.map(locData => ({ ...locData, - stations: locData.stations.filter(station => (station.ps?.toLowerCase() === rdsPs.replace(/ /g, '_').toLowerCase())) + stations: locData.stations.filter(station => + station.ps?.toLowerCase().includes( + rdsPs.replace(/ /g, '_').toLowerCase() + ) ?? false + ) })).filter(locData => locData.stations.length > 0); if (extraFilteredLocations.length > 0) filteredLocations = extraFilteredLocations; @@ -370,8 +373,7 @@ function haversine(lat1, lon1, lat2, lon2) { const y = Math.sin(dLon) * Math.cos(deg2rad(lat2)); const x = Math.cos(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) - Math.sin(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(dLon); - const azimuth = Math.atan2(y, x); - const azimuthDegrees = (azimuth * 180 / Math.PI + 360) % 360; + const azimuthDegrees = (Math.atan2(y, x) * 180 / Math.PI + 360) % 360; return { distanceKm: distance, diff --git a/web/403.ejs b/web/403.ejs index 2e388b7..819ec0d 100644 --- a/web/403.ejs +++ b/web/403.ejs @@ -21,7 +21,7 @@

- There's a possibility you were kicked by the system.
+ The service has rejected you.
Please try again later.

<% if (reason) { %> diff --git a/web/index.ejs b/web/index.ejs index 00b45f4..da377d5 100644 --- a/web/index.ejs +++ b/web/index.ejs @@ -562,7 +562,7 @@
Support the developer!
-
+
Join our FMDX.org Discord community!