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 hostnameView on GitHub (pinned to 988cbb0330)
Solutions
- Fix the address string to include an explicit port, e.g. "localhost:8080" or "[::1]:8080" for IPv6.
- If the address comes from config or environment, normalize it before dialing: run net.SplitHostPort yourself and default the port when missing.
- For gRPC, ensure the target includes the port (grpc.NewClient("dns:///host:443", ...)) or use a resolver dial option.
- 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
- Normalize addresses with net.JoinHostPort; bracket IPv6 literals.
- Pass u.Host from a parsed URL, never the full URL string, to dial functions.
- Default missing ports explicitly in configuration loading.
- Validate all dial addresses at config-load time, not at request time.
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
- service unreachable: %w
- zip URL blocked by SSRF check: %v
- connection blocked: port %s is restricted
- connection blocked: hostname %s is restricted
- connection blocked: hostname suffix %s is restricted
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/37e8dc0fbcb66d7d.
Report an issue: GitHub.