JuliusBrussee/caveman · error

ssrf: host %q resolved to no addresses

Error message

ssrf: host %q resolved to no addresses

What it means

Pre-flight check in ssrf.ValidateURL: the hostname resolved without error but LookupIPAddr returned zero addresses. Treated as a hard failure because there is nothing to range-check — an empty answer must not be allowed to slip past the SSRF policy.

Source

Thrown at shared/platform/ssrf/ssrf.go:223

	}

	// "localhost" is explicitly blocked regardless of what DNS says — unless a
	// self-hosted operator allowlisted it (resolution still runs, so every
	// resolved address is range-checked below like any other).
	if strings.EqualFold(host, "localhost") && !(!cfg.ManagedMode && isInAllowList(host, port, cfg.AllowList)) {
		return fmt.Errorf("ssrf: host %q is blocked (loopback)", host)
	}

	// Resolve ALL addresses the hostname currently maps to.  A hostname that
	// returns even one blocked address is rejected (defense-in-depth against
	// split-horizon / DNS rebinding scenarios where the pre-flight check and
	// the dial see different answers).
	addrs, err := net.DefaultResolver.LookupIPAddr(ctx, host)
	if err != nil {
		return fmt.Errorf("ssrf: DNS resolution failed for %q: %w", host, err)
	}
	if len(addrs) == 0 {
		return fmt.Errorf("ssrf: host %q resolved to no addresses", host)
	}

	for _, ia := range addrs {
		a, ok := netip.AddrFromSlice(ia.IP)
		if !ok {
			return fmt.Errorf("ssrf: could not parse resolved IP %v for host %q", ia.IP, host)
		}
		a = a.Unmap() // normalise ::ffff:x.x.x.x → x.x.x.x
		if err := checkAddr(a, host, port, cfg); err != nil {
			return err
		}
	}
	return nil
}

// validateHostInput rejects URL/userinfo-shaped values before they reach DNS
// or an error formatter. IP literals (including zoned IPv6) are handled by
// netip.ParseAddr and may contain colons or a zone identifier.

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Verify the name actually has A/AAAA records: `dig example.com A +short` should list addresses.
  2. Fix the DNS record or use the correct service hostname.
  3. If using a search-domain/suffix setup, use the FQDN with trailing dot if the bare short name resolves empty.
Defensive patterns

Strategy: validation

Validate before calling

addrs, err := net.DefaultResolver.LookupIPAddr(ctx, u.Hostname())
if err == nil && len(addrs) == 0 {
    return fmt.Errorf("hostname has no address records")
}

Prevention

When it happens

Trigger: Calling ssrf.ValidateURL for a name whose DNS answer contains no A/AAAA records (e.g. only MX/NS records exist, or a stale empty RRset), or a resolver returning an empty NOERROR answer.

Common situations: Zone exists but the A record was deleted; hostname taken from an email domain (MX-only); CDN/registrar placeholder zones with no address records.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/05e41e8b00d2e231. Report an issue: GitHub.