JuliusBrussee/caveman · error

ssrf: could not parse resolved IP %v for host %q

Error message

ssrf: could not parse resolved IP %v for host %q

What it means

Defensive parse failure in ssrf.ValidateURL: the resolver returned an IP that netip.AddrFromSlice could not convert. In practice this is near-impossible (LookupIPAddr yields 4/16-byte slices), so seeing it indicates resolver corruption or a pathological environment rather than a user mistake.

Source

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

		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.
func validateHostInput(host string) error {
	if host == "" {
		return errors.New("ssrf: invalid host")
	}
	if _, err := netip.ParseAddr(host); err == nil {
		return nil

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Check what the resolver actually returns for the host (`dig example.com A`, `dig example.com AAAA`).
  2. Point the process at a trustworthy recursive resolver (system/default) instead of the odd one in front of it.
  3. If this appears in tests, fix the DNS stub to return valid net.IP values.
Defensive patterns

Strategy: try-catch

Try / catch

if err := ssrf.ValidateURL(ctx, raw, cfg); err != nil {
    if strings.Contains(err.Error(), "could not parse resolved IP") {
        // environment/resolver fault, not user input: alert ops
    }
}

Prevention

When it happens

Trigger: A DNS response handing back a malformed address that still parses into a non-4/non-16 byte slice; essentially only reachable with a broken or hostile resolver in front of the process.

Common situations: Custom/captive DNS servers returning non-standard records; exotic test doubles stubbing LookupIPAddr with invalid data. Production users effectively never see this.

Related errors


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