JuliusBrussee/caveman · error

ssrf: invalid dial address

Error message

ssrf: invalid dial address

What it means

Dial-time enforcement inside dialContextWith: the address being connected to failed validation before the dialer was invoked. The hostname was resolved and every returned address is checked against SSRF block lists, then only a validated IP literal is dialed; this error means that validation failed at dial time.

Source

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

	dialer := &net.Dialer{
		Timeout:   connectTimeout,
		KeepAlive: 30 * time.Second,
	}
	return dialContextWith(cfg, net.DefaultResolver.LookupNetIP, dialer.DialContext)
}

type lookupNetIPFunc func(context.Context, string, string) ([]netip.Addr, error)
type rawDialContextFunc func(context.Context, string, string) (net.Conn, error)

// dialContextWith resolves each hostname exactly once, validates every returned
// address, then dials a validated IP literal. net.Transport passes hostnames to
// DialContext; validating the hostname and handing it to net.Dialer would cause
// a second DNS lookup and reopen the DNS-rebinding window.
func dialContextWith(cfg Config, lookup lookupNetIPFunc, dial rawDialContextFunc) func(ctx context.Context, network, addr string) (net.Conn, error) {
	return func(ctx context.Context, network, addr string) (net.Conn, error) {
		host, port, err := net.SplitHostPort(addr)
		if err != nil {
			return nil, errors.New("ssrf: invalid dial address")
		}
		if err := validateHostInput(host); err != nil {
			return nil, err
		}
		if cfg.ManagedMode && port != "443" {
			return nil, errors.New("ssrf: managed mode requires port 443")
		}

		if parsed, parseErr := netip.ParseAddr(host); parseErr == nil {
			parsed = parsed.WithZone("").Unmap()
			if err := checkAddr(parsed, host, port, cfg); err != nil {
				return nil, err
			}
			return dial(ctx, network, net.JoinHostPort(parsed.String(), port))
		}

		if strings.EqualFold(host, "localhost") && !(!cfg.ManagedMode && isInAllowList(host, port, cfg.AllowList)) {
			return nil, fmt.Errorf("ssrf: host %q is blocked (loopback)", host)

View on GitHub (pinned to 766dce6b13)

Solutions

  1. Compare the dial-time failure with the pre-flight ValidateURL result to find where DNS rebinding or a changed hostname bypassed pre-flight checks
  2. Pin or re-validate the upstream configuration before retrying the connection
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at shared/platform/ssrf/ssrf.go:398 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18). Data as JSON: /api/errors/c026b4a4a90fa468. Report an issue: GitHub.