{"record":{"id":"e56f665d2fd1f9bf","repo":"Hmbown/CodeWhale","slug":"mcp-http-dns-resolved-to-a-restricted-ip-address","errorCode":null,"errorMessage":"MCP HTTP DNS resolved to a restricted IP address","messagePattern":"MCP HTTP DNS resolved to a restricted IP address","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp/http_client.rs","lineNumber":297,"sourceCode":"    let Some(host) = url.host_str() else {\n        return false;\n    };\n    let host = host.trim_end_matches('.');\n    host.eq_ignore_ascii_case(\"localhost\")\n        || host.to_ascii_lowercase().ends_with(\".localhost\")\n        || host\n            .trim_start_matches('[')\n            .trim_end_matches(']')\n            .parse::<IpAddr>()\n            .is_ok_and(|ip| is_restricted_ip(&ip))\n}\n\nfn validated_public_address(addresses: &[SocketAddr]) -> Result<SocketAddr> {\n    if addresses\n        .iter()\n        .any(|address| is_restricted_ip(&address.ip()))\n    {\n        bail!(\"MCP HTTP DNS resolved to a restricted IP address\");\n    }\n    addresses\n        .first()\n        .copied()\n        .context(\"MCP HTTP DNS resolved to no addresses\")\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use tokio::io::{AsyncReadExt, AsyncWriteExt};\n    use tokio::net::TcpListener;\n\n    fn client(url: &str, runtime_added: bool) -> McpHttpClient {\n        McpHttpClient::new(\n            url,\n            runtime_added,\n            false,","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/mcp/http_client.rs#L279-L315","documentation":"validated_public_address rejects DNS resolutions for an MCP HTTP destination when ANY resolved socket address is a restricted IP. This is the hostname path of the same SSRF defense as error 1150: even if you use a domain name, the client pins DNS resolution and refuses to connect if the resolver returns private/loopback/link-local addresses. It prevents DNS-based SSRF and rebinding to internal networks.","triggerScenarios":"Connecting to an MCP server via hostname whose DNS A/AAAA records (resolved through the pinned resolver in public_dns_pin) include a restricted IP such as 10.x.x.x, 127.0.0.1, 169.254.169.254, or ::1.","commonSituations":"A DNS record pointing at an internal service, split-horizon DNS returning private IPs in the current environment, /etc/hosts or corporate resolver entries mapping the host to a local address, or a tunneling setup resolving to loopback.","solutions":["Update DNS so the MCP server hostname resolves to a public, non-restricted address","Use a different hostname that resolves publicly for the same server","If the server is intentionally internal, expose it via an allowed public endpoint instead of pointing DNS at the private IP","Verify with dig/nslookup what the hostname resolves to in this environment and correct the record"],"exampleFix":"// before\nmcp.example.com.  IN A  10.0.0.5\n// after\nmcp.example.com.  IN A  203.0.113.10","handlingStrategy":"validation","validationCode":"async fn resolves_to_public(host: &str) -> bool {\n    tokio::net::lookup_host((host, 443u16)).await\n        .map(|addrs| addrs.all(|a| !is_restricted_ip(&a.ip())))\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match client_for_target(&url).await {\n    Err(e) if e.to_string().contains(\"DNS resolved to a restricted IP\") => {\n        eprintln!(\"host resolves to a private address; fix DNS or use a public endpoint\");\n    }\n    other => other?,\n}","preventionTips":["dig/nslookup the MCP hostname before configuring it and confirm all records are public","Beware split-horizon DNS and /etc/hosts overrides mapping hosts to private IPs","Prefer HTTPS public endpoints for MCP servers"],"tags":["ssrf","dns","mcp","network"],"backgroundTag":"invalid-argument-value","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T21:17:16.096Z"}