diff --git a/api/gateway/tron/internal/service/gateway/tronclient/client.go b/api/gateway/tron/internal/service/gateway/tronclient/client.go index 5cb9131a..c586d235 100644 --- a/api/gateway/tron/internal/service/gateway/tronclient/client.go +++ b/api/gateway/tron/internal/service/gateway/tronclient/client.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "fmt" "math/big" + "net" "net/url" "strings" "time" @@ -27,7 +28,7 @@ type Client struct { } // NewClient creates a new TRON gRPC client connected to the given endpoint. -func NewClient(grpcURL string, timeout time.Duration, authToken string) (*Client, error) { +func NewClient(grpcURL string, timeout time.Duration, authToken string, forceIPv4 bool) (*Client, error) { if grpcURL == "" { return nil, merrors.InvalidArgument("tronclient: grpc url is required") } @@ -50,6 +51,9 @@ func NewClient(grpcURL string, timeout time.Duration, authToken string) (*Client } opts := []grpc.DialOption{transportCreds} + if forceIPv4 { + opts = append(opts, grpc.WithContextDialer(grpcForceIPv4Dialer)) + } if token := strings.TrimSpace(authToken); token != "" { opts = append(opts, grpc.WithUnaryInterceptor(grpcTokenUnaryInterceptor(token)), @@ -67,6 +71,40 @@ func NewClient(grpcURL string, timeout time.Duration, authToken string) (*Client }, nil } +func grpcForceIPv4Dialer(ctx context.Context, address string) (net.Conn, error) { + host, port, err := net.SplitHostPort(address) + if err != nil { + return nil, err + } + + // Resolve IPv4 only + ips, err := net.DefaultResolver.LookupIP(ctx, "ip4", host) + if err != nil { + return nil, err + } + if len(ips) == 0 { + return nil, merrors.Internal(fmt.Sprintf("no IPv4 address found for %s", host)) + } + + var dialer net.Dialer + var lastErr error + for _, ip := range ips { + target := net.JoinHostPort(ip.String(), port) + conn, err := dialer.DialContext(ctx, "tcp4", target) + if err == nil { + return conn, nil + } + lastErr = err + if ctx.Err() != nil { + break + } + } + if lastErr != nil { + return nil, merrors.Internal(fmt.Sprintf("failed to dial any IPv4 address for %s: %v", host, lastErr)) + } + return nil, merrors.Internal(fmt.Sprintf("failed to dial IPv4 address for %s", host)) +} + func normalizeGRPCAddress(grpcURL string) (string, bool, error) { target := strings.TrimSpace(grpcURL) useTLS := false