Hmbown/CodeWhale · error
MCP HTTP DNS resolved to a restricted IP address
Error message
MCP HTTP DNS resolved to a restricted IP address
What it means
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.
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
Example fix
// before mcp.example.com. IN A 10.0.0.5 // after mcp.example.com. IN A 203.0.113.10
Defensive patterns
Strategy: validation
Validate before calling
async fn resolves_to_public(host: &str) -> bool {
tokio::net::lookup_host((host, 443u16)).await
.map(|addrs| addrs.all(|a| !is_restricted_ip(&a.ip())))
.unwrap_or(false)
} Try / catch
match client_for_target(&url).await {
Err(e) if e.to_string().contains("DNS resolved to a restricted IP") => {
eprintln!("host resolves to a private address; fix DNS or use a public endpoint");
}
other => other?,
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- MCP HTTP destination is a restricted IP address
- MCP HTTP destination blocked by network policy
- MCP SSE connect cancelled before the request completed
- MCP SSE endpoint is not same-origin as — refusing to send…
- outbound origin must be a public service host
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/e56f665d2fd1f9bf.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/mcp/http_client.rs:297
let Some(host) = url.host_str() else {
return false;
};
let host = host.trim_end_matches('.');
host.eq_ignore_ascii_case("localhost")
|| host.to_ascii_lowercase().ends_with(".localhost")
|| host
.trim_start_matches('[')
.trim_end_matches(']')
.parse::<IpAddr>()
.is_ok_and(|ip| is_restricted_ip(&ip))
}
fn validated_public_address(addresses: &[SocketAddr]) -> Result<SocketAddr> {
if addresses
.iter()
.any(|address| is_restricted_ip(&address.ip()))
{
bail!("MCP HTTP DNS resolved to a restricted IP address");
}
addresses
.first()
.copied()
.context("MCP HTTP DNS resolved to no addresses")
}
#[cfg(test)]
mod tests {
use super::*;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener;
fn client(url: &str, runtime_added: bool) -> McpHttpClient {
McpHttpClient::new(
url,
runtime_added,
false,View on GitHub (pinned to 73e0f67d83)