Skip to content

Commit 8729ef4

Browse files
committed
chipingress: add forceIPV4 option
1 parent 694386f commit 8729ef4

1 file changed

Lines changed: 48 additions & 0 deletions

File tree

pkg/chipingress/client.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,15 @@ type clientConfig struct {
4444
headerProvider HeaderProvider
4545
insecureConnection bool
4646
host string
47+
forceIPV4 bool
4748
}
4849

4950
func newClientConfig(host string) *clientConfig {
5051
cfg := &clientConfig{
5152
headerProvider: nil,
5253
perRPCCredentials: nil,
5354
host: host,
55+
forceIPV4: false,
5456
}
5557
WithInsecureConnection()(cfg) // Default to insecure connection
5658
return cfg
@@ -72,6 +74,11 @@ func NewClient(address string, opts ...Opt) (Client, error) {
7274
grpcOpts := []grpc.DialOption{
7375
grpc.WithTransportCredentials(cfg.transportCredentials),
7476
}
77+
// Add our custom dialer if IPv4 is forced
78+
if cfg.forceIPV4 {
79+
grpcOpts = append(grpcOpts, grpc.WithContextDialer(forceIPV4Dialer))
80+
}
81+
7582
// Auth
7683
if cfg.perRPCCredentials != nil {
7784
grpcOpts = append(grpcOpts, grpc.WithPerRPCCredentials(cfg.perRPCCredentials))
@@ -110,6 +117,13 @@ func (c *client) Close() error {
110117
return c.conn.Close()
111118
}
112119

120+
// WithForceIPV4 forces the client to use IPv4 for connections.
121+
func WithForceIPV4() Opt {
122+
return func(c *clientConfig) {
123+
c.forceIPV4 = true
124+
}
125+
}
126+
113127
// WithBasicAuth sets the basic-auth credentials for the ChipIngress service.
114128
// Default is to require TLS for security.
115129
func WithBasicAuth(user, pass string) Opt {
@@ -172,6 +186,40 @@ func newHeaderInterceptor(provider HeaderProvider) grpc.UnaryClientInterceptor {
172186
}
173187
}
174188

189+
// forceIPV4Dialer is a custom dialer that resolves a hostname and forces the connection over IPv4.
190+
func forceIPV4Dialer(ctx context.Context, addr string) (net.Conn, error) {
191+
host, port, err := net.SplitHostPort(addr)
192+
if err != nil {
193+
return nil, fmt.Errorf("failed to split host and port: %w", err)
194+
}
195+
196+
// Resolve the hostname to a list of IP addresses.
197+
ips, err := net.LookupIP(host)
198+
if err != nil {
199+
return nil, fmt.Errorf("failed to resolve IP addresses for host %s: %w", host, err)
200+
}
201+
202+
var ipv4Addr string
203+
// Find the first IPv4 address.
204+
for _, ip := range ips {
205+
if ip.To4() != nil {
206+
ipv4Addr = ip.String()
207+
break
208+
}
209+
}
210+
211+
if ipv4Addr == "" {
212+
return nil, fmt.Errorf("no IPv4 address found for host: %s", host)
213+
}
214+
215+
// Create the new address with the resolved IPv4 and original port.
216+
ipv4AddrWithPort := net.JoinHostPort(ipv4Addr, port)
217+
218+
// Dial the new IPv4 address, explicitly using "tcp4".
219+
var d net.Dialer
220+
return d.DialContext(ctx, "tcp4", ipv4AddrWithPort)
221+
}
222+
175223
// NewEvent creates a new CloudEvent with the specified domain, entity, payload, and optional attributes.
176224
func NewEvent(domain, entity string, payload []byte, attributes map[string]any) (CloudEvent, error) {
177225

0 commit comments

Comments
 (0)