From dda0fa2bdee96bffbc1f83356c6d62f94d4de7aa Mon Sep 17 00:00:00 2001 From: Fangliding Date: Thu, 15 Oct 2026 14:54:15 +0800 Subject: [PATCH] Make dial fail on interface bind err --- proxy/tun/handler.go | 9 +++-- transport/internet/sockopt.go | 19 +++++++++++ transport/internet/sockopt_darwin.go | 6 ++-- transport/internet/sockopt_linux.go | 2 +- transport/internet/sockopt_windows.go | 10 +++--- transport/internet/system_dialer.go | 48 +++++++++++++++++---------- 6 files changed, 66 insertions(+), 28 deletions(-) diff --git a/proxy/tun/handler.go b/proxy/tun/handler.go index c13625b0b..c9465f66c 100644 --- a/proxy/tun/handler.go +++ b/proxy/tun/handler.go @@ -125,7 +125,8 @@ func (t *Handler) Start() error { errors.LogInfo(context.Background(), "[tun] falied to set interface > iface == nil") return errors.New("iface not found") } - return c.Control(func(fd uintptr) { + var controlErr, sockErr error + controlErr = c.Control(func(fd uintptr) { addrPort, _ := netip.ParseAddrPort(address) // skip loopback if addrPort.Addr().IsLoopback() || strings.HasPrefix(strings.ToLower(address), "localhost:") { @@ -133,9 +134,13 @@ func (t *Handler) Start() error { } err := setinterface(network, address, fd, iface) if err != nil { - errors.LogInfoInner(context.Background(), err, "[tun] falied to set interface") + sockErr = errors.New("[tun] falied to set interface").Base(err) } }) + if sockErr != nil { + return sockErr + } + return controlErr }) } diff --git a/transport/internet/sockopt.go b/transport/internet/sockopt.go index d79191ab2..cc92b3bca 100644 --- a/transport/internet/sockopt.go +++ b/transport/internet/sockopt.go @@ -28,3 +28,22 @@ func (v *SocketConfig) ParseTFOValue() int { } return tfo } + +// Dial should fail on bind errors, return this to indicate such errors. +type BindError struct { + err error +} + +func (e *BindError) Unwrap() error { + return e.err +} + +func (e *BindError) Error() string { + return "failed to bind to interface: " + e.err.Error() +} + +func NewBindError(err error) *BindError { + return &BindError{ + err: err, + } +} diff --git a/transport/internet/sockopt_darwin.go b/transport/internet/sockopt_darwin.go index 2ac78ab4a..762fadfec 100644 --- a/transport/internet/sockopt_darwin.go +++ b/transport/internet/sockopt_darwin.go @@ -136,15 +136,15 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf if config.Interface != "" { iface, err := net.InterfaceByName(config.Interface) if err != nil { - return errors.New("failed to get interface ", config.Interface).Base(err) + return NewBindError(err) } if network == "tcp6" || network == "udp6" { if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, iface.Index); err != nil { - return errors.New("failed to set IPV6_BOUND_IF").Base(err) + return NewBindError(err) } } else { if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, iface.Index); err != nil { - return errors.New("failed to set IP_BOUND_IF").Base(err) + return NewBindError(err) } } } diff --git a/transport/internet/sockopt_linux.go b/transport/internet/sockopt_linux.go index 7c614d9fb..251f44c71 100644 --- a/transport/internet/sockopt_linux.go +++ b/transport/internet/sockopt_linux.go @@ -22,7 +22,7 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf if config.Interface != "" { if err := syscall.BindToDevice(int(fd), config.Interface); err != nil { - return errors.New("failed to set Interface").Base(err) + return NewBindError(err) } } diff --git a/transport/internet/sockopt_windows.go b/transport/internet/sockopt_windows.go index dd1df0bd2..7d4a0db84 100644 --- a/transport/internet/sockopt_windows.go +++ b/transport/internet/sockopt_windows.go @@ -35,7 +35,7 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf if config.Interface != "" { inf, err := net.InterfaceByName(config.Interface) if err != nil { - return errors.New("failed to find the interface").Base(err) + return NewBindError(err) } // easy way to check if the address is ipv4 isV4 := strings.Contains(address, ".") @@ -47,20 +47,20 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf binary.BigEndian.PutUint32(bytes[:], uint32(inf.Index)) idx := *(*uint32)(unsafe.Pointer(&bytes[0])) if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IP, IP_UNICAST_IF, int(idx)); err != nil { - return errors.New("failed to set IP_UNICAST_IF").Base(err) + return NewBindError(err) } if ip := net.ParseIP(host); ip != nil && ip.IsMulticast() && isUDPSocket(network) { if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, int(idx)); err != nil { - return errors.New("failed to set IP_MULTICAST_IF").Base(err) + return NewBindError(err) } } } else { if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IPV6, IPV6_UNICAST_IF, inf.Index); err != nil { - return errors.New("failed to set IPV6_UNICAST_IF").Base(err) + return NewBindError(err) } if ip := net.ParseIP(host); ip != nil && ip.IsMulticast() && isUDPSocket(network) { if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, inf.Index); err != nil { - return errors.New("failed to set IPV6_MULTICAST_IF").Base(err) + return NewBindError(err) } } } diff --git a/transport/internet/system_dialer.go b/transport/internet/system_dialer.go index bfe3fb592..009d7646e 100644 --- a/transport/internet/system_dialer.go +++ b/transport/internet/system_dialer.go @@ -68,19 +68,27 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne } } var lc net.ListenConfig - lc.Control = func(network, address string, c syscall.RawConn) error { - for _, ctl := range Controllers { - if err := ctl(network, address, c); err != nil { - errors.LogInfoInner(ctx, err, "failed to apply external controller") - } - } - return c.Control(func(fd uintptr) { - if sockopt != nil { - if err := applyOutboundSocketOptions(network, destAddr.String(), fd, sockopt); err != nil { - errors.LogInfo(ctx, err, "failed to apply socket options") + if sockopt != nil || len(Controllers) > 0 { + lc.Control = func(network, address string, c syscall.RawConn) error { + for _, ctl := range Controllers { + if err := ctl(network, address, c); err != nil { + return errors.New("failed to apply external controller").Base(err) } } - }) + if sockopt != nil { + var sockErr, controlErr error + controlErr = c.Control(func(fd uintptr) { + if sockErr = applyOutboundSocketOptions(network, destAddr.String(), fd, sockopt); sockErr != nil { + errors.LogInfoInner(ctx, sockErr, "failed to apply socket options") + } + }) + if _, ok := sockErr.(*BindError); ok { + return sockErr + } + return controlErr + } + return nil + } } packetConn, err := lc.ListenPacket(ctx, srcAddr.Network(), srcAddr.String()) if err != nil { @@ -128,16 +136,22 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne dialer.Control = func(network, address string, c syscall.RawConn) error { for _, ctl := range Controllers { if err := ctl(network, address, c); err != nil { - errors.LogInfoInner(ctx, err, "failed to apply external controller") + return errors.New("failed to apply external controller").Base(err) } } - return c.Control(func(fd uintptr) { - if sockopt != nil { - if err := applyOutboundSocketOptions(network, address, fd, sockopt); err != nil { - errors.LogInfoInner(ctx, err, "failed to apply socket options") + if sockopt != nil { + var sockErr, controlErr error + controlErr = c.Control(func(fd uintptr) { + if sockErr = applyOutboundSocketOptions(network, address, fd, sockopt); sockErr != nil { + errors.LogInfoInner(ctx, sockErr, "failed to apply socket options") } + }) + if _, ok := sockErr.(*BindError); ok { + return sockErr } - }) + return controlErr + } + return nil } }