mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-11-10 21:21:02 +08:00
Fix dialing IPv6 link-local addresses without zone
This commit is contained in:
2 files changed
+51
-28
No files matched your search
@@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -39,6 +40,7 @@ type DefaultDialer struct {
|
||||
udpAddr4 string
|
||||
udpAddr6 string
|
||||
netns string
|
||||
bindInterface string
|
||||
autoDetectBindFunc control.Func
|
||||
connectionManager adapter.ConnectionManager
|
||||
networkManager adapter.NetworkManager
|
||||
@@ -62,6 +64,7 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
||||
dialer net.Dialer
|
||||
listener net.ListenConfig
|
||||
interfaceFinder control.InterfaceFinder
|
||||
bindInterface string
|
||||
networkStrategy *C.NetworkStrategy
|
||||
defaultNetworkStrategy bool
|
||||
networkType []C.InterfaceType
|
||||
@@ -86,6 +89,7 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
||||
}
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
bindInterface = options.BindInterface
|
||||
}
|
||||
if options.RoutingMark > 0 {
|
||||
if !C.IsLinux {
|
||||
@@ -107,6 +111,7 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
||||
bindFunc := control.BindToInterface(networkManager.InterfaceFinder(), defaultOptions.BindInterface, -1)
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
bindInterface = defaultOptions.BindInterface
|
||||
} else if networkManager.AutoDetectInterface() && !disableDefaultBind && options.NetNs == "" {
|
||||
if platformInterface != nil && platformInterface.UsePlatformNetworkInterfaces() {
|
||||
networkStrategy = (*C.NetworkStrategy)(options.NetworkStrategy)
|
||||
@@ -242,6 +247,7 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
||||
udpAddr4: udpAddr4,
|
||||
udpAddr6: udpAddr6,
|
||||
netns: options.NetNs,
|
||||
bindInterface: bindInterface,
|
||||
autoDetectBindFunc: autoDetectBindFunc,
|
||||
connectionManager: connectionManager,
|
||||
networkManager: networkManager,
|
||||
@@ -279,6 +285,19 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
||||
return nil, E.New("domain not resolved")
|
||||
}
|
||||
if d.networkStrategy == nil {
|
||||
if d.netns == "" && address.Addr.Zone() == "" && isLinkScoped(address.Addr) {
|
||||
if d.bindInterface != "" {
|
||||
address.Addr = address.Addr.WithZone(d.bindInterface)
|
||||
} else if d.autoDetectBindFunc != nil || d.defaultNetworkStrategy {
|
||||
iif, err := d.networkManager.InterfaceFinder().ByAddr(address.Addr)
|
||||
if err != nil {
|
||||
iif = d.networkManager.InterfaceMonitor().DefaultInterface()
|
||||
}
|
||||
if iif != nil {
|
||||
address.Addr = address.Addr.WithZone(strconv.Itoa(iif.Index))
|
||||
}
|
||||
}
|
||||
}
|
||||
conn, err := listener.ListenNetworkNamespace[net.Conn](ctx, d.netns, func() (net.Conn, error) {
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkUDP:
|
||||
@@ -300,6 +319,10 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
||||
}
|
||||
}
|
||||
|
||||
func isLinkScoped(addr netip.Addr) bool {
|
||||
return addr.Is6() && (addr.IsLinkLocalUnicast() || addr.IsLinkLocalMulticast() || addr.IsInterfaceLocalMulticast())
|
||||
}
|
||||
|
||||
func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network string, address M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||
if strategy == nil {
|
||||
strategy = d.networkStrategy
|
||||
@@ -329,9 +352,9 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
|
||||
err error
|
||||
)
|
||||
if !fastFallback {
|
||||
conn, isPrimary, err = d.dialParallelInterface(ctx, dialer, network, address.String(), *strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||
conn, isPrimary, err = d.dialParallelInterface(ctx, dialer, network, address, *strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||
} else {
|
||||
conn, isPrimary, err = d.dialParallelInterfaceFastFallback(ctx, dialer, network, address.String(), *strategy, interfaceType, fallbackInterfaceType, fallbackDelay, d.networkLastFallback.Store)
|
||||
conn, isPrimary, err = d.dialParallelInterfaceFastFallback(ctx, dialer, network, address, *strategy, interfaceType, fallbackInterfaceType, fallbackDelay, d.networkLastFallback.Store)
|
||||
}
|
||||
if err != nil {
|
||||
// bind interface failed on legacy xiaomi systems
|
||||
|
||||
@@ -3,6 +3,7 @@ package dialer
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -11,10 +12,11 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func (d *DefaultDialer) dialParallelInterface(ctx context.Context, dialer net.Dialer, network string, addr string, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, bool, error) {
|
||||
func (d *DefaultDialer) dialParallelInterface(ctx context.Context, dialer net.Dialer, network string, destination M.Socksaddr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, bool, error) {
|
||||
primaryInterfaces, fallbackInterfaces := selectInterfaces(d.networkManager, strategy, interfaceType, fallbackInterfaceType)
|
||||
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
||||
return nil, false, E.New("no available network interface")
|
||||
@@ -31,13 +33,9 @@ func (d *DefaultDialer) dialParallelInterface(ctx context.Context, dialer net.Di
|
||||
} else {
|
||||
iif = fallbackInterfaces[0]
|
||||
}
|
||||
perNetDialer := dialer
|
||||
if defaultInterface == nil || iif.Index != defaultInterface.Index {
|
||||
perNetDialer.Control = control.Append(perNetDialer.Control, control.BindToInterface(nil, iif.Name, iif.Index))
|
||||
}
|
||||
conn, err := perNetDialer.DialContext(ctx, network, addr)
|
||||
conn, err := dialInterface(ctx, dialer, network, destination, iif, defaultInterface)
|
||||
if err != nil {
|
||||
return nil, false, E.Cause(err, "dial ", iif.Name, " (", iif.Index, ")")
|
||||
return nil, false, err
|
||||
}
|
||||
return conn, primary, nil
|
||||
}
|
||||
@@ -53,14 +51,10 @@ func (d *DefaultDialer) dialParallelInterface(ctx context.Context, dialer net.Di
|
||||
}
|
||||
results := make(chan dialResult) // unbuffered
|
||||
startRacer := func(ctx context.Context, primary bool, iif adapter.NetworkInterface) {
|
||||
perNetDialer := dialer
|
||||
if defaultInterface == nil || iif.Index != defaultInterface.Index {
|
||||
perNetDialer.Control = control.Append(perNetDialer.Control, control.BindToInterface(nil, iif.Name, iif.Index))
|
||||
}
|
||||
conn, err := perNetDialer.DialContext(ctx, network, addr)
|
||||
conn, err := dialInterface(ctx, dialer, network, destination, iif, defaultInterface)
|
||||
if err != nil {
|
||||
select {
|
||||
case results <- dialResult{error: E.Cause(err, "dial ", iif.Name, " (", iif.Index, ")"), primary: primary}:
|
||||
case results <- dialResult{error: err, primary: primary}:
|
||||
case <-returned:
|
||||
}
|
||||
} else {
|
||||
@@ -109,7 +103,7 @@ func (d *DefaultDialer) dialParallelInterface(ctx context.Context, dialer net.Di
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, dialer net.Dialer, network string, addr string, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration, resetFastFallback func(time.Time)) (net.Conn, bool, error) {
|
||||
func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, dialer net.Dialer, network string, destination M.Socksaddr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration, resetFastFallback func(time.Time)) (net.Conn, bool, error) {
|
||||
primaryInterfaces, fallbackInterfaces := selectInterfaces(d.networkManager, strategy, interfaceType, fallbackInterfaceType)
|
||||
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
||||
return nil, false, E.New("no available network interface")
|
||||
@@ -126,13 +120,9 @@ func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, d
|
||||
} else {
|
||||
iif = fallbackInterfaces[0]
|
||||
}
|
||||
perNetDialer := dialer
|
||||
if defaultInterface == nil || iif.Index != defaultInterface.Index {
|
||||
perNetDialer.Control = control.Append(perNetDialer.Control, control.BindToInterface(nil, iif.Name, iif.Index))
|
||||
}
|
||||
conn, err := perNetDialer.DialContext(ctx, network, addr)
|
||||
conn, err := dialInterface(ctx, dialer, network, destination, iif, defaultInterface)
|
||||
if err != nil {
|
||||
return nil, false, E.Cause(err, "dial ", iif.Name, " (", iif.Index, ")")
|
||||
return nil, false, err
|
||||
}
|
||||
return conn, primary, nil
|
||||
}
|
||||
@@ -149,14 +139,10 @@ func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, d
|
||||
startAt := time.Now()
|
||||
results := make(chan dialResult) // unbuffered
|
||||
startRacer := func(ctx context.Context, primary bool, iif adapter.NetworkInterface) {
|
||||
perNetDialer := dialer
|
||||
if defaultInterface == nil || iif.Index != defaultInterface.Index {
|
||||
perNetDialer.Control = control.Append(perNetDialer.Control, control.BindToInterface(nil, iif.Name, iif.Index))
|
||||
}
|
||||
conn, err := perNetDialer.DialContext(ctx, network, addr)
|
||||
conn, err := dialInterface(ctx, dialer, network, destination, iif, defaultInterface)
|
||||
if err != nil {
|
||||
select {
|
||||
case results <- dialResult{error: E.Cause(err, "dial ", iif.Name, " (", iif.Index, ")"), primary: primary}:
|
||||
case results <- dialResult{error: err, primary: primary}:
|
||||
case <-returned:
|
||||
}
|
||||
} else {
|
||||
@@ -191,6 +177,20 @@ func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, d
|
||||
return nil, false, E.Errors(errors...)
|
||||
}
|
||||
|
||||
func dialInterface(ctx context.Context, dialer net.Dialer, network string, destination M.Socksaddr, iif adapter.NetworkInterface, defaultInterface *control.Interface) (net.Conn, error) {
|
||||
if defaultInterface == nil || iif.Index != defaultInterface.Index {
|
||||
dialer.Control = control.Append(dialer.Control, control.BindToInterface(nil, iif.Name, iif.Index))
|
||||
}
|
||||
if destination.Addr.Zone() == "" && isLinkScoped(destination.Addr) {
|
||||
destination.Addr = destination.Addr.WithZone(strconv.Itoa(iif.Index))
|
||||
}
|
||||
conn, err := dialer.DialContext(ctx, network, destination.String())
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "dial ", iif.Name, " (", iif.Index, ")")
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (d *DefaultDialer) serialInterfaceControl(strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType) control.Func {
|
||||
return func(network, address string, conn syscall.RawConn) error {
|
||||
primaryInterfaces, fallbackInterfaces := selectInterfaces(d.networkManager, strategy, interfaceType, fallbackInterfaceType)
|
||||
|
||||
Reference in new issue
Block a user