mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-06-26 12:23:12 +03:00
Handle TUN loopback in direct outbound
This commit is contained in:
@@ -182,10 +182,10 @@ func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listene
|
|||||||
|
|
||||||
func selectInterfaces(networkManager adapter.NetworkManager, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType) (primaryInterfaces []adapter.NetworkInterface, fallbackInterfaces []adapter.NetworkInterface) {
|
func selectInterfaces(networkManager adapter.NetworkManager, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType) (primaryInterfaces []adapter.NetworkInterface, fallbackInterfaces []adapter.NetworkInterface) {
|
||||||
interfaces := networkManager.NetworkInterfaces()
|
interfaces := networkManager.NetworkInterfaces()
|
||||||
myInterface := networkManager.InterfaceMonitor().MyInterface()
|
myInterfaces := networkManager.InterfaceMonitor().MyInterfaces()
|
||||||
if myInterface != "" {
|
if len(myInterfaces) > 0 {
|
||||||
interfaces = common.Filter(interfaces, func(it adapter.NetworkInterface) bool {
|
interfaces = common.Filter(interfaces, func(it adapter.NetworkInterface) bool {
|
||||||
return it.Name != myInterface
|
return !common.Contains(myInterfaces, it.Name)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
switch strategy {
|
switch strategy {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
@@ -77,12 +78,12 @@ func dnsReadConfig(ctx context.Context, _ string) *dnsConfig {
|
|||||||
}{ifName: windows.UTF16PtrToString(address.FriendlyName), Addr: dnsServerAddr})
|
}{ifName: windows.UTF16PtrToString(address.FriendlyName), Addr: dnsServerAddr})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var myInterface string
|
var myInterfaces []string
|
||||||
if networkManager := service.FromContext[adapter.NetworkManager](ctx); networkManager != nil {
|
if networkManager := service.FromContext[adapter.NetworkManager](ctx); networkManager != nil {
|
||||||
myInterface = networkManager.InterfaceMonitor().MyInterface()
|
myInterfaces = networkManager.InterfaceMonitor().MyInterfaces()
|
||||||
}
|
}
|
||||||
for _, address := range dnsAddresses {
|
for _, address := range dnsAddresses {
|
||||||
if address.ifName == myInterface {
|
if common.Contains(myInterfaces, address.ifName) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
conf.servers = append(conf.servers, net.JoinHostPort(address.String(), "53"))
|
conf.servers = append(conf.servers, net.JoinHostPort(address.String(), "53"))
|
||||||
|
|||||||
@@ -189,8 +189,8 @@ func (s *interfaceMonitorStub) UnregisterCallback(element *list.Element[tun.Defa
|
|||||||
func (s *interfaceMonitorStub) RegisterMyInterface(interfaceName string) {
|
func (s *interfaceMonitorStub) RegisterMyInterface(interfaceName string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *interfaceMonitorStub) MyInterface() string {
|
func (s *interfaceMonitorStub) MyInterfaces() []string {
|
||||||
return ""
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatConfig(configContent string) (*StringBox, error) {
|
func FormatConfig(configContent string) (*StringBox, error) {
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ var (
|
|||||||
|
|
||||||
type platformDefaultInterfaceMonitor struct {
|
type platformDefaultInterfaceMonitor struct {
|
||||||
*platformInterfaceWrapper
|
*platformInterfaceWrapper
|
||||||
logger logger.Logger
|
logger logger.Logger
|
||||||
callbacks list.List[tun.DefaultInterfaceUpdateCallback]
|
callbacks list.List[tun.DefaultInterfaceUpdateCallback]
|
||||||
myInterface string
|
myInterfaces []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *platformDefaultInterfaceMonitor) Start() error {
|
func (m *platformDefaultInterfaceMonitor) Start() error {
|
||||||
@@ -106,11 +106,11 @@ func (m *platformDefaultInterfaceMonitor) updateDefaultInterface(interfaceName s
|
|||||||
func (m *platformDefaultInterfaceMonitor) RegisterMyInterface(interfaceName string) {
|
func (m *platformDefaultInterfaceMonitor) RegisterMyInterface(interfaceName string) {
|
||||||
m.defaultInterfaceAccess.Lock()
|
m.defaultInterfaceAccess.Lock()
|
||||||
defer m.defaultInterfaceAccess.Unlock()
|
defer m.defaultInterfaceAccess.Unlock()
|
||||||
m.myInterface = interfaceName
|
m.myInterfaces = append(m.myInterfaces, interfaceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *platformDefaultInterfaceMonitor) MyInterface() string {
|
func (m *platformDefaultInterfaceMonitor) MyInterfaces() []string {
|
||||||
m.defaultInterfaceAccess.Lock()
|
m.defaultInterfaceAccess.Lock()
|
||||||
defer m.defaultInterfaceAccess.Unlock()
|
defer m.defaultInterfaceAccess.Unlock()
|
||||||
return m.myInterface
|
return m.myInterfaces
|
||||||
}
|
}
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -39,7 +39,7 @@ require (
|
|||||||
github.com/sagernet/sing-shadowsocks v0.2.8
|
github.com/sagernet/sing-shadowsocks v0.2.8
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1
|
github.com/sagernet/sing-shadowsocks2 v0.2.1
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
|
||||||
github.com/sagernet/sing-tun v0.8.9
|
github.com/sagernet/sing-tun v0.8.10
|
||||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1
|
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1
|
||||||
github.com/sagernet/smux v1.5.50-sing-box-mod.1
|
github.com/sagernet/smux v1.5.50-sing-box-mod.1
|
||||||
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.7
|
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.7
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -248,8 +248,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnq
|
|||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
|
||||||
github.com/sagernet/sing-tun v0.8.9 h1:ixFKKUGdVcJl4wb0xbL36hobiw9l6DIH497EQf5ILpM=
|
github.com/sagernet/sing-tun v0.8.10 h1:1Kc1hr8/2YcnQrW00fWxt6LTnj1xYQO96ZcrhV7V3As=
|
||||||
github.com/sagernet/sing-tun v0.8.9/go.mod h1:QvarqUtHfj1ULaRR+6kZOS/OoCE+pYGq67A5tyIy+dQ=
|
github.com/sagernet/sing-tun v0.8.10/go.mod h1:QvarqUtHfj1ULaRR+6kZOS/OoCE+pYGq67A5tyIy+dQ=
|
||||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 h1:aSwUNYUkVyVvdmBSufR8/nRFonwJeKSIROxHcm5br9o=
|
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 h1:aSwUNYUkVyVvdmBSufR8/nRFonwJeKSIROxHcm5br9o=
|
||||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY=
|
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY=
|
||||||
github.com/sagernet/smux v1.5.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478=
|
github.com/sagernet/smux v1.5.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478=
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterOutbound(registry *outbound.Registry) {
|
func RegisterOutbound(registry *outbound.Registry) {
|
||||||
@@ -37,10 +38,12 @@ type Outbound struct {
|
|||||||
outbound.Adapter
|
outbound.Adapter
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
|
network adapter.NetworkManager
|
||||||
dialer dialer.ParallelInterfaceDialer
|
dialer dialer.ParallelInterfaceDialer
|
||||||
domainStrategy C.DomainStrategy
|
domainStrategy C.DomainStrategy
|
||||||
fallbackDelay time.Duration
|
fallbackDelay time.Duration
|
||||||
isEmpty bool
|
isEmpty bool
|
||||||
|
myAddresses common.TypedValue[[]netip.Prefix]
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (adapter.Outbound, error) {
|
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (adapter.Outbound, error) {
|
||||||
@@ -61,6 +64,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeDirect, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMP}, options.DialerOptions),
|
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeDirect, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMP}, options.DialerOptions),
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
network: service.FromContext[adapter.NetworkManager](ctx),
|
||||||
//nolint:staticcheck
|
//nolint:staticcheck
|
||||||
domainStrategy: C.DomainStrategy(options.DomainStrategy),
|
domainStrategy: C.DomainStrategy(options.DomainStrategy),
|
||||||
fallbackDelay: time.Duration(options.FallbackDelay),
|
fallbackDelay: time.Duration(options.FallbackDelay),
|
||||||
@@ -74,7 +78,48 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
return outbound, nil
|
return outbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) Start(stage adapter.StartStage) error {
|
||||||
|
switch stage {
|
||||||
|
case adapter.StartStatePostStart, adapter.StartStateStarted:
|
||||||
|
h.fetchMyAddresses()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) fetchMyAddresses() {
|
||||||
|
if len(h.myAddresses.Load()) > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
myInterfaceNames := h.network.InterfaceMonitor().MyInterfaces()
|
||||||
|
if len(myInterfaceNames) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var myAddresses []netip.Prefix
|
||||||
|
for _, myInterfaceName := range myInterfaceNames {
|
||||||
|
myInterface, err := h.network.InterfaceFinder().ByName(myInterfaceName)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
myAddresses = append(myAddresses, myInterface.Addresses...)
|
||||||
|
}
|
||||||
|
h.myAddresses.Store(myAddresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Outbound) isMyLoopbackAddress(addresses ...netip.Addr) bool {
|
||||||
|
for _, prefix := range h.myAddresses.Load() {
|
||||||
|
for _, address := range addresses {
|
||||||
|
if prefix.Addr() != address && prefix.Contains(address) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
|
if h.isMyLoopbackAddress(destination.Addr) {
|
||||||
|
return nil, E.New("loopback connection to TUN range")
|
||||||
|
}
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.Tag()
|
metadata.Outbound = h.Tag()
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
@@ -89,6 +134,9 @@ func (h *Outbound) DialContext(ctx context.Context, network string, destination
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
|
if h.isMyLoopbackAddress(destination.Addr) {
|
||||||
|
return nil, E.New("loopback connection to TUN range")
|
||||||
|
}
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.Tag()
|
metadata.Outbound = h.Tag()
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
@@ -111,6 +159,9 @@ func (h *Outbound) NewDirectRouteConnection(metadata adapter.InboundContext, rou
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Outbound) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) {
|
func (h *Outbound) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) {
|
||||||
|
if h.isMyLoopbackAddress(destinationAddresses...) {
|
||||||
|
return nil, E.New("loopback connection to TUN range")
|
||||||
|
}
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.Tag()
|
metadata.Outbound = h.Tag()
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
@@ -125,6 +176,9 @@ func (h *Outbound) DialParallel(ctx context.Context, network string, destination
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy *C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy *C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||||
|
if h.isMyLoopbackAddress(destinationAddresses...) {
|
||||||
|
return nil, E.New("loopback connection to TUN range")
|
||||||
|
}
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.Tag()
|
metadata.Outbound = h.Tag()
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
@@ -139,6 +193,9 @@ func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, dest
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Outbound) ListenSerialNetworkPacket(ctx context.Context, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy *C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error) {
|
func (h *Outbound) ListenSerialNetworkPacket(ctx context.Context, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy *C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error) {
|
||||||
|
if h.isMyLoopbackAddress(destinationAddresses...) {
|
||||||
|
return nil, netip.Addr{}, E.New("loopback connection to TUN range")
|
||||||
|
}
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.Tag()
|
metadata.Outbound = h.Tag()
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ func NewNetworkInterfaceAddressItem(networkManager adapter.NetworkManager, inter
|
|||||||
|
|
||||||
func (r *NetworkInterfaceAddressItem) Match(metadata *adapter.InboundContext) bool {
|
func (r *NetworkInterfaceAddressItem) Match(metadata *adapter.InboundContext) bool {
|
||||||
interfaces := r.networkManager.NetworkInterfaces()
|
interfaces := r.networkManager.NetworkInterfaces()
|
||||||
myInterface := r.networkManager.InterfaceMonitor().MyInterface()
|
myInterfaces := r.networkManager.InterfaceMonitor().MyInterfaces()
|
||||||
match:
|
match:
|
||||||
for ifType, addresses := range r.interfaceAddresses {
|
for ifType, addresses := range r.interfaceAddresses {
|
||||||
for _, networkInterface := range interfaces {
|
for _, networkInterface := range interfaces {
|
||||||
if networkInterface.Name == myInterface {
|
if common.Contains(myInterfaces, networkInterface.Name) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if networkInterface.Type != ifType {
|
if networkInterface.Type != ifType {
|
||||||
|
|||||||
Reference in New Issue
Block a user