const express = require("express"); const cors = require("cors"); const helmet = require("helmet"); const rateLimit = require("express-rate-limit"); const app = express(); const PORT = 4000; app.disable("x-powered-by"); app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'"], styleSrc: ["'self'"], imgSrc: ["'self'", "data:"], connectSrc: ["'self'"], objectSrc: ["'none'"], baseUri: ["'self'"], frameAncestors: ["'none'"] } } })); app.use(cors()); app.use(express.json({ limit: "10kb" })); app.use(express.static("public")); app.use(express.static("public")); app.set("trust proxy", true); const apiLimiter = rateLimit({ windowMs: 60 * 1000, max: 60, standardHeaders: true, legacyHeaders: false, message: { status: "error", error: "Too many requests. Please try again later." } }); app.use("/api/check", apiLimiter); const agents = [ { id: "iran-tehran-parsvds", sourceType: "owned", sourceLabel: "Our server", priority: 1, country: "Iran", city: "Tehran", datacenter: "Parsvds", flag: "IR", url: "http://127.0.0.1:3000" } ]; const externalProviders = [ { id: "google-public-dns", sourceType: "external", sourceLabel: "External check", priority: 100, country: "Global", city: "Google Public DNS", datacenter: "Google", flag: "GOOGLE", provider: "google-doh" } ]; const ALLOWED_TYPES = ["A", "AAAA", "NS", "CNAME", "MX"]; function isValidDomain(domain) { if (!domain || typeof domain !== "string") return false; if (domain.length > 253) return false; if (domain.includes("..")) return false; if (domain.startsWith(".") || domain.endsWith(".")) return false; return /^(?!-)([a-zA-Z0-9-]{1,63}\.)+[a-zA-Z]{2,63}$/.test(domain); } function buildLocation(provider) { return { id: provider.id, sourceType: provider.sourceType, sourceLabel: provider.sourceLabel, priority: provider.priority, country: provider.country, city: provider.city, datacenter: provider.datacenter, flag: provider.flag }; } function dnsTypeToGoogleType(type) { const map = { A: 1, NS: 2, CNAME: 5, MX: 15, AAAA: 28 }; return map[type] || type; } function googleStatusToError(status) { const map = { 1: "DNS query format error.", 2: "Destination DNS server did not respond with a valid answer.", 3: "Domain was not found.", 4: "DNS request type is not supported.", 5: "DNS request was refused." }; return map[status] || `DNS error status ${status}`; } function parseGoogleAnswers(data, type) { if (!data.Answer || !Array.isArray(data.Answer)) { return []; } const typeMap = { A: 1, NS: 2, CNAME: 5, MX: 15, AAAA: 28 }; const wantedType = typeMap[type]; return data.Answer .filter(answer => answer.type === wantedType) .map(answer => { if (type === "MX") { const parts = String(answer.data).trim().split(/\s+/); return { priority: Number(parts[0]), exchange: parts.slice(1).join(" ").replace(/\.$/, "") }; } return String(answer.data).replace(/\.$/, ""); }); } async function fetchJsonWithTimeout(url, timeoutMs = 4000) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), timeoutMs); try { const response = await fetch(url, { signal: controller.signal, headers: { accept: "application/json" } }); return await response.json(); } finally { clearTimeout(timeout); } } async function queryOwnedAgent(agent, domain, type) { const startTime = Date.now(); try { const url = `${agent.url}/resolve?domain=${encodeURIComponent(domain)}&type=${encodeURIComponent(type)}`; const data = await fetchJsonWithTimeout(url, 8000); return { location: buildLocation(agent), query: { domain, type }, result: data.result, gatewayTimeMs: Date.now() - startTime }; } catch (err) { return { location: buildLocation(agent), query: { domain, type }, result: { status: "error", error: err.name === "AbortError" ? "Agent request timed out." : "Agent request failed.", answers: [], responseTimeMs: null }, gatewayTimeMs: Date.now() - startTime }; } } async function queryGoogleDns(provider, domain, type) { const startTime = Date.now(); try { const googleType = dnsTypeToGoogleType(type); const url = `https://dns.google/resolve?name=${encodeURIComponent(domain)}&type=${encodeURIComponent(googleType)}`; const data = await fetchJsonWithTimeout(url, 5000); const answers = parseGoogleAnswers(data, type); const success = data.Status === 0 && answers.length > 0; return { location: buildLocation(provider), query: { domain, type }, result: { status: success ? "success" : "error", error: success ? null : googleStatusToError(data.Status), answers, responseTimeMs: Date.now() - startTime }, gatewayTimeMs: Date.now() - startTime }; } catch (err) { return { location: buildLocation(provider), query: { domain, type }, result: { status: "error", error: err.name === "AbortError" ? "External DNS request timed out." : "External DNS request failed.", answers: [], responseTimeMs: null }, gatewayTimeMs: Date.now() - startTime }; } } app.get("/api/client-ip", (req, res) => { const ip = req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || req.ip || req.socket.remoteAddress || "Unknown"; res.json({ ip }); }); app.get("/api/locations", (req, res) => { res.json({ locations: [...agents, ...externalProviders].map(provider => buildLocation(provider)) }); }); app.get("/api/check", async (req, res) => { const domain = String(req.query.domain || "").trim().toLowerCase(); const type = String(req.query.type || "A").toUpperCase(); if (!domain) { return res.status(400).json({ status: "error", error: "domain is required" }); } if (!isValidDomain(domain)) { return res.status(400).json({ status: "error", error: "invalid domain" }); } if (!ALLOWED_TYPES.includes(type)) { return res.status(400).json({ status: "error", error: "unsupported record type", allowedTypes: ALLOWED_TYPES }); } const ownedResults = await Promise.all( agents.map(agent => queryOwnedAgent(agent, domain, type)) ); const externalResults = await Promise.all( externalProviders.map(provider => queryGoogleDns(provider, domain, type)) ); const results = [...ownedResults, ...externalResults]; results.sort((a, b) => { return (a.location.priority || 999) - (b.location.priority || 999); }); res.json({ status: "success", query: { domain, type }, results }); }); app.listen(PORT, "127.0.0.1", () => { console.log(`DNS Checker backend running on port ${PORT}`); });