Tencent/WeKnora · error

invalid address %s: %w

Error message

invalid address %s: %w

What it means

SSRFSafeDialContext could not parse the address into host and port because net.SplitHostPort failed. The dialer requires addresses in "host:port" form (with brackets for IPv6 literals) to run its dial-time SSRF checks; a malformed address means it cannot even begin validation. The wrapped error (%w) states whether the problem is a missing port, too many colons, or similar.

Source

Thrown at internal/utils/security.go:795

// upstream should share one NewSSRFSafeTransport via NewSSRFSafeHTTPClientWithTransport instead.
func NewSSRFSafeHTTPClient(config SSRFSafeHTTPClientConfig) *http.Client {
	return NewSSRFSafeHTTPClientWithTransport(config, NewSSRFSafeTransport(config))
}

// SSRFSafeGRPCDialer is compatible with grpc.WithContextDialer and pins DNS
// answers the same way as SSRFSafeDialContext.
func SSRFSafeGRPCDialer(ctx context.Context, addr string) (net.Conn, error) {
	return SSRFSafeDialContext(ctx, "tcp", addr)
}

// SSRFSafeDialContext is a custom dial function that validates the resolved IP addresses
// before establishing a connection. This provides an additional layer of SSRF protection
// against DNS rebinding attacks during the connection phase.
func SSRFSafeDialContext(ctx context.Context, network, addr string) (net.Conn, error) {
	// Parse host and port
	host, port, err := net.SplitHostPort(addr)
	if err != nil {
		return nil, fmt.Errorf("invalid address %s: %w", addr, err)
	}

	// Whitelisted hosts bypass all dial-time SSRF checks, consistent with
	// ValidateURLForSSRF which skips isSSRFSafeURL for whitelisted hosts.
	// NOTE: This intentionally relaxes DNS-rebinding protection for whitelisted
	// hosts. Admins must ensure whitelisted domains are under their control.
	if IsSystemProxy(addr) || IsSSRFWhitelisted(host) {
		dialer := &net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}
		return dialer.DialContext(ctx, network, addr)
	}
	if restrictedPorts[port] {
		return nil, fmt.Errorf("connection blocked: port %s is restricted", port)
	}

	// Check if the host is a restricted hostname

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Fix the address string to include an explicit port, e.g. "localhost:8080" or "[::1]:8080" for IPv6.
  2. If the address comes from config or environment, normalize it before dialing: run net.SplitHostPort yourself and default the port when missing.
  3. For gRPC, ensure the target includes the port (grpc.NewClient("dns:///host:443", ...)) or use a resolver dial option.
  4. Strip any scheme before passing the address: parse with net/url and pass u.Host, not the full URL.

Example fix

// before
dialer := &net.Dialer{...}
conn, err := utils.SSRFSafeDialContext(ctx, "tcp", "my-internal-service")

// after
addr := "my-internal-service"
if _, _, err := net.SplitHostPort(addr); err != nil {
    addr = net.JoinHostPort(addr, "443")
}
conn, err := utils.SSRFSafeDialContext(ctx, "tcp", addr)
Defensive patterns

Strategy: validation

Validate before calling

if _, _, err := net.SplitHostPort(addr); err != nil {
    return fmt.Errorf("dial address %q is not host:port: %w", addr, err)
}

Try / catch

conn, err := utils.SSRFSafeDialContext(ctx, "tcp", addr)
if err != nil && strings.Contains(err.Error(), "invalid address") {
    return nil, fmt.Errorf("misconfigured dial address %q (expected host:port): %w", addr, err)
}

Prevention

When it happens

Trigger: Passing an address without a port ("localhost"), an unbracketed IPv6 literal ("::1:8080"), an empty string, or an address with extra colons into SSRFSafeDialContext, SSRFSafeGRPCDialer, or a custom dial function that delegates to it (e.g. DialContext on http.Transport wired to it). TestSSRFSafeDialContextRejectsRestrictedPortAtFinalSink calls it with the addresses produced by the transport, so a malformed addr from configuration triggers this.

Common situations: Configuring a gRPC target as "myhost" instead of "myhost:443"; hand-building proxy addresses and forgetting the port; pasting a URL ("http://host:port") where a host:port dial address is expected; IPv6 endpoints missing square brackets.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/37e8dc0fbcb66d7d. Report an issue: GitHub.