From 2985f279c679e59d3d858af4c9578dc9e4f86895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20B=C3=BCtof?= Date: Tue, 2 Dec 2025 17:44:40 +0100 Subject: [PATCH] Implement experimental DNS pre-resolution Add custom axios interceptor to resolve DNS manually before requests. This avoids problems with axios' built-in DNS resolution in cases of partial resolution failures. --- server/Server.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/server/Server.js b/server/Server.js index d6f748a1..b4f9647c 100644 --- a/server/Server.js +++ b/server/Server.js @@ -7,6 +7,7 @@ const fs = require('./libs/fsExtra') const fileUpload = require('./libs/expressFileupload') const cookieParser = require('cookie-parser') const axios = require('axios') +const dns = require('dns').promises const { version } = require('../package.json') @@ -86,6 +87,49 @@ class Server { global.DisableSsrfRequestFilter = (url) => whitelistedUrls.includes(new URL(url).hostname) } } + + if (process.env.EXP_DNS_RESOLUTION === '1') { + // https://github.com/advplyr/audiobookshelf/pull/3754 + Logger.info(`[Server] Experimental DNS Resolution Enabled`) + + // Resolve DNS using dns package before making request + axios.interceptors.request.use(async (config) => { + try { + const urlObj = new URL(config.url) + const hostname = urlObj.hostname + let resolved = false + + const resolvers = [ + { protocol: 'IPv4', method: dns.resolve4, format: (ip) => ip }, + { protocol: 'IPv6', method: dns.resolve6, format: (ip) => `[${ip}]` } + ] + if (process.env.PREFER_IPV6 === '1') { + resolvers.reverse() + } + + for (const { protocol, method, format } of resolvers) { + const addresses = await method(hostname).catch(() => null) + if (addresses?.length > 0) { + const ip = format(addresses[0]) + urlObj.hostname = ip + config.url = urlObj.toString() + config.headers = { ...config.headers, Host: hostname } + Logger.debug(`[Server] Resolved ${hostname} -> ${addresses[0]} (${protocol})`) + resolved = true + break + } + } + + if (!resolved) { + throw new Error(`Could not resolve hostname ${hostname} to any IP address`) + } + } catch (err) { + Logger.warn(`[Server] DNS pre-resolution error: ${err.message}`) + } + return config + }) + } + global.PodcastDownloadTimeout = toNumber(process.env.PODCAST_DOWNLOAD_TIMEOUT, 30000) global.MaxFailedEpisodeChecks = toNumber(process.env.MAX_FAILED_EPISODE_CHECKS, 24)