{"record":{"id":"2594def34a1eff24","repo":"koala73/worldmonitor","slug":"serverurl-dns-resolution-failed","errorCode":null,"errorMessage":"serverUrl DNS resolution failed","messagePattern":"serverUrl DNS resolution failed","errorType":"exception","errorClass":"McpProxySsrfError","httpStatus":null,"severity":"error","filePath":"api/mcp-proxy.ts","lineNumber":344,"sourceCode":"  return records.flat();\n}\n\nasync function assertServerUrlSafe(url, signal) {\n  signal?.throwIfAborted();\n  const hostname = url.hostname.toLowerCase();\n  if (BLOCKED_HOSTNAMES.has(hostname)) {\n    throw new McpProxySsrfError('serverUrl hostname is blocked');\n  }\n  if (isBlockedResolvedAddress(hostname)) {\n    throwBlockedAddress(hostname);\n  }\n\n  let resolvedAddresses;\n  try {\n    resolvedAddresses = await defaultResolveHostname(hostname, signal);\n  } catch (error) {\n    signal?.throwIfAborted();\n    throw new McpProxySsrfError('serverUrl DNS resolution failed', { cause: error });\n  }\n  signal?.throwIfAborted();\n\n  if (!resolvedAddresses.length) {\n    throw new McpProxySsrfError('serverUrl DNS resolution returned no addresses');\n  }\n\n  const blocked = resolvedAddresses.find(isBlockedResolvedAddress);\n  if (blocked) {\n    throwBlockedAddress(blocked);\n  }\n\n  return { url, resolvedAddresses };\n}\n\n// Vercel Edge fetch does not expose a Node-style lookup/socket hook, so this\n// proxy CANNOT pin the TLS connection to a previously vetted address. There is\n// no way to guarantee that the IP we validated is the IP fetch() ultimately","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/api/mcp-proxy.ts#L326-L362","documentation":"assertServerUrlSafe resolves the serverUrl hostname via DNS before fetching, to verify no resolved address is private/reserved. If defaultResolveHostname throws (NXDOMAIN, resolver outage, transient DNS failure), it throws McpProxySsrfError('serverUrl DNS resolution failed', { cause }). A hostname that resolves to zero addresses also fails with a sibling message.","triggerScenarios":"Registering/calling an MCP server whose serverUrl hostname does not exist (typo, deleted DNS record, split-horizon internal name visible only inside a private VPC), or where the DNS resolver itself is failing/timing out.","commonSituations":"Typos in serverUrl hostnames; pointing at internal-only DNS names from a public edge function; DNS provider outage; IPv6 AAAA-only records failing in an IPv4 resolver; DNS record recently deleted after a service rename.","solutions":["Verify the hostname resolves publicly: run dig/nslookup on it from outside your network","Fix typos in serverUrl or restore the deleted DNS record","If the host is internal-only, expose it publicly — the proxy requires public DNS and public addresses","Check error.cause for the resolver error code (ENOTFOUND vs ETIMEOUT) to distinguish bad name from resolver outage"],"exampleFix":"// before\nserverUrl: 'https://mcp.corp-internal.local/mcp' // ENOTFOUND publicly\n// after\nserverUrl: 'https://mcp.acme.dev/mcp' // public A/AAAA records","handlingStrategy":"validation","validationCode":"// pre-flight DNS check before registering\nimport { lookup } from 'node:dns/promises';\nasync function hostnameResolves(u) { try { return (await lookup(new URL(u).hostname)).address.length > 0; } catch { return false; } }","typeGuard":"null","tryCatchPattern":"try { await validateServerUrl(serverUrl); } catch (e) { if (e instanceof McpProxySsrfError && e.message.includes('DNS')) return { error: 'dns_failure', hostname: new URL(serverUrl).hostname }; throw e; }","preventionTips":["Run dig/nslookup against serverUrl hostnames before registering them","Avoid internal-only DNS names for public proxies","Monitor resolver health if you own the DNS provider path","Check error.cause for ENOTFOUND (bad name) vs ETIMEOUT (resolver outage)"],"tags":["dns","ssrf","network","proxy","url"],"backgroundTag":"dns-resolution-failed","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}