diff --git a/common/dialer/detour.go b/common/dialer/detour.go index 81600913..faa40e62 100644 --- a/common/dialer/detour.go +++ b/common/dialer/detour.go @@ -6,21 +6,27 @@ import ( "sync" "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) type DetourDialer struct { - router adapter.Router - detour string - dialer N.Dialer - initOnce sync.Once - initErr error + router adapter.Router + detour string + allowNestedDirect bool + dialer N.Dialer + initOnce sync.Once + initErr error } -func NewDetour(router adapter.Router, detour string) N.Dialer { - return &DetourDialer{router: router, detour: detour} +func NewDetour(router adapter.Router, detour string, allowNestedDirect bool) N.Dialer { + return &DetourDialer{ + router: router, + detour: detour, + allowNestedDirect: allowNestedDirect, + } } func (d *DetourDialer) Start() error { @@ -30,10 +36,17 @@ func (d *DetourDialer) Start() error { func (d *DetourDialer) Dialer() (N.Dialer, error) { d.initOnce.Do(func() { - var loaded bool - d.dialer, loaded = d.router.Outbound(d.detour) + var ( + dialer adapter.Outbound + loaded bool + ) + dialer, loaded = d.router.Outbound(d.detour) if !loaded { d.initErr = E.New("outbound detour not found: ", d.detour) + } else if !d.allowNestedDirect && dialer.Type() == C.TypeDirect { + d.initErr = E.New("using a direct outbound as a detour is illegal") + } else { + d.dialer = dialer } }) return d.dialer, d.initErr diff --git a/common/dialer/dialer.go b/common/dialer/dialer.go index bbb4b3a9..753d1d7f 100644 --- a/common/dialer/dialer.go +++ b/common/dialer/dialer.go @@ -23,7 +23,7 @@ func New(router adapter.Router, options option.DialerOptions) (N.Dialer, error) return nil, err } } else { - dialer = NewDetour(router, options.Detour) + dialer = NewDetour(router, options.Detour, false) } domainStrategy := dns.DomainStrategy(options.DomainStrategy) if domainStrategy != dns.DomainStrategyAsIS || options.Detour == "" { diff --git a/outbound/direct.go b/outbound/direct.go index 3925d2c8..5b84ce9b 100644 --- a/outbound/direct.go +++ b/outbound/direct.go @@ -35,6 +35,9 @@ type Direct struct { func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) { options.UDPFragmentDefault = true + if options.Detour != "" { + return nil, E.New("detour option for direct outbound is illegal") + } outboundDialer, err := dialer.New(router, options.DialerOptions) if err != nil { return nil, err diff --git a/route/router.go b/route/router.go index b7aee514..5979bf4d 100644 --- a/route/router.go +++ b/route/router.go @@ -195,7 +195,7 @@ func NewRouter( if server.Detour == "" { detour = dialer.NewRouter(router) } else { - detour = dialer.NewDetour(router, server.Detour) + detour = dialer.NewDetour(router, server.Detour, true) } switch server.Address { case "local":