diff --git a/common/dialer/default.go b/common/dialer/default.go index 49bd145c..77536c43 100644 --- a/common/dialer/default.go +++ b/common/dialer/default.go @@ -2,8 +2,10 @@ package dialer import ( "context" + "errors" "net" "net/netip" + "syscall" "time" "github.com/sagernet/sing-box/adapter" @@ -26,20 +28,21 @@ var ( ) type DefaultDialer struct { - dialer4 tcpDialer - dialer6 tcpDialer - udpDialer4 net.Dialer - udpDialer6 net.Dialer - udpListener net.ListenConfig - udpAddr4 string - udpAddr6 string - isWireGuardListener bool - networkManager adapter.NetworkManager - networkStrategy *C.NetworkStrategy - networkType []C.InterfaceType - fallbackNetworkType []C.InterfaceType - networkFallbackDelay time.Duration - networkLastFallback atomic.TypedValue[time.Time] + dialer4 tcpDialer + dialer6 tcpDialer + udpDialer4 net.Dialer + udpDialer6 net.Dialer + udpListener net.ListenConfig + udpAddr4 string + udpAddr6 string + isWireGuardListener bool + networkManager adapter.NetworkManager + networkStrategy *C.NetworkStrategy + defaultNetworkStrategy bool + networkType []C.InterfaceType + fallbackNetworkType []C.InterfaceType + networkFallbackDelay time.Duration + networkLastFallback atomic.TypedValue[time.Time] } func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDialer, error) { @@ -47,13 +50,14 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial platformInterface := service.FromContext[platform.Interface](ctx) var ( - dialer net.Dialer - listener net.ListenConfig - interfaceFinder control.InterfaceFinder - networkStrategy *C.NetworkStrategy - networkType []C.InterfaceType - fallbackNetworkType []C.InterfaceType - networkFallbackDelay time.Duration + dialer net.Dialer + listener net.ListenConfig + interfaceFinder control.InterfaceFinder + networkStrategy *C.NetworkStrategy + defaultNetworkStrategy bool + networkType []C.InterfaceType + fallbackNetworkType []C.InterfaceType + networkFallbackDelay time.Duration ) if networkManager != nil { interfaceFinder = networkManager.InterfaceFinder() @@ -98,6 +102,7 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial networkStrategy = (*C.NetworkStrategy)(options.NetworkStrategy) if networkStrategy == nil { networkStrategy = common.Ptr(C.NetworkStrategyDefault) + defaultNetworkStrategy = true } networkType = common.Map(options.NetworkType, option.InterfaceType.Build) fallbackNetworkType = common.Map(options.FallbackNetworkType, option.InterfaceType.Build) @@ -192,19 +197,20 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial return nil, err } return &DefaultDialer{ - dialer4: tcpDialer4, - dialer6: tcpDialer6, - udpDialer4: udpDialer4, - udpDialer6: udpDialer6, - udpListener: listener, - udpAddr4: udpAddr4, - udpAddr6: udpAddr6, - isWireGuardListener: options.IsWireGuardListener, - networkManager: networkManager, - networkStrategy: networkStrategy, - networkType: networkType, - fallbackNetworkType: fallbackNetworkType, - networkFallbackDelay: networkFallbackDelay, + dialer4: tcpDialer4, + dialer6: tcpDialer6, + udpDialer4: udpDialer4, + udpDialer6: udpDialer6, + udpListener: listener, + udpAddr4: udpAddr4, + udpAddr6: udpAddr6, + isWireGuardListener: options.IsWireGuardListener, + networkManager: networkManager, + networkStrategy: networkStrategy, + defaultNetworkStrategy: defaultNetworkStrategy, + networkType: networkType, + fallbackNetworkType: fallbackNetworkType, + networkFallbackDelay: networkFallbackDelay, }, nil } @@ -265,7 +271,13 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin conn, isPrimary, err = d.dialParallelInterfaceFastFallback(ctx, dialer, network, address.String(), *strategy, interfaceType, fallbackInterfaceType, fallbackDelay, d.networkLastFallback.Store) } if err != nil { - return nil, err + // bind interface failed on legacy xiaomi systems + if d.defaultNetworkStrategy && errors.Is(err, syscall.EPERM) { + d.networkStrategy = nil + return d.DialContext(ctx, network, address) + } else { + return nil, err + } } if !fastFallback && !isPrimary { d.networkLastFallback.Store(time.Now()) @@ -307,7 +319,17 @@ func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destina if destination.IsIPv4() && !destination.Addr.IsUnspecified() { network += "4" } - return trackPacketConn(d.listenSerialInterfacePacket(ctx, d.udpListener, network, "", *strategy, interfaceType, fallbackInterfaceType, fallbackDelay)) + packetConn, err := d.listenSerialInterfacePacket(ctx, d.udpListener, network, "", *strategy, interfaceType, fallbackInterfaceType, fallbackDelay) + if err != nil { + // bind interface failed on legacy xiaomi systems + if d.defaultNetworkStrategy && errors.Is(err, syscall.EPERM) { + d.networkStrategy = nil + return d.ListenPacket(ctx, destination) + } else { + return nil, err + } + } + return trackPacketConn(packetConn, nil) } func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) { diff --git a/common/dialer/default_parallel_interface.go b/common/dialer/default_parallel_interface.go index 269546a4..92e3535e 100644 --- a/common/dialer/default_parallel_interface.go +++ b/common/dialer/default_parallel_interface.go @@ -35,7 +35,7 @@ func (d *DefaultDialer) dialParallelInterface(ctx context.Context, dialer net.Di conn, err := perNetDialer.DialContext(ctx, network, addr) if err != nil { select { - case results <- dialResult{error: E.Cause(err, "dial ", iif.Name, " (", iif.Name, ")"), primary: primary}: + case results <- dialResult{error: E.Cause(err, "dial ", iif.Name, " (", iif.Index, ")"), primary: primary}: case <-returned: } } else { @@ -107,7 +107,7 @@ func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, d conn, err := perNetDialer.DialContext(ctx, network, addr) if err != nil { select { - case results <- dialResult{error: E.Cause(err, "dial ", iif.Name, " (", iif.Name, ")"), primary: primary}: + case results <- dialResult{error: E.Cause(err, "dial ", iif.Name, " (", iif.Index, ")"), primary: primary}: case <-returned: } } else { @@ -157,7 +157,7 @@ func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listene if err == nil { return conn, nil } - errors = append(errors, E.Cause(err, "listen ", primaryInterface.Name, " (", primaryInterface.Name, ")")) + errors = append(errors, E.Cause(err, "listen ", primaryInterface.Name, " (", primaryInterface.Index, ")")) } for _, fallbackInterface := range fallbackInterfaces { perNetListener := listener @@ -166,7 +166,7 @@ func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listene if err == nil { return conn, nil } - errors = append(errors, E.Cause(err, "listen ", fallbackInterface.Name, " (", fallbackInterface.Name, ")")) + errors = append(errors, E.Cause(err, "listen ", fallbackInterface.Name, " (", fallbackInterface.Index, ")")) } return nil, E.Errors(errors...) }