Files
sing-box-extended-mirror/dns/transport_manager.go
T

185 lines
5.5 KiB
Go

package dns
import (
"context"
"os"
"strings"
"sync"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
)
var _ adapter.DNSTransportManager = (*TransportManager)(nil)
type TransportManager struct {
registry adapter.DNSTransportRegistry
outbound adapter.OutboundManager
defaultTag string
access sync.RWMutex
transports []adapter.DNSTransport
transportByTag map[string]adapter.DNSTransport
defaultTransport adapter.DNSTransport
defaultTransportFallback func() (adapter.DNSTransport, error)
fakeIPTransport adapter.FakeIPTransport
}
func NewTransportManager(registry adapter.DNSTransportRegistry, outbound adapter.OutboundManager, defaultTag string) *TransportManager {
return &TransportManager{
registry: registry,
outbound: outbound,
defaultTag: defaultTag,
transportByTag: make(map[string]adapter.DNSTransport),
}
}
func (m *TransportManager) Initialize(defaultTransportFallback func() (adapter.DNSTransport, error)) {
m.defaultTransportFallback = defaultTransportFallback
}
func (m *TransportManager) Start(stage adapter.StartStage, scope *adapter.Scope) error {
m.access.Lock()
if stage == adapter.StartStateInitialize {
if m.defaultTag != "" && m.defaultTransport == nil {
m.access.Unlock()
return E.New("default DNS server not found: ", m.defaultTag)
}
if m.defaultTransport == nil {
defaultTransport, err := m.defaultTransportFallback()
if err != nil {
m.access.Unlock()
return E.Cause(err, "default DNS server fallback")
}
m.transports = append(m.transports, defaultTransport)
m.transportByTag[defaultTransport.Tag()] = defaultTransport
m.defaultTransport = defaultTransport
}
}
transports := m.transports
m.access.Unlock()
if stage == adapter.StartStateStart {
return m.startTransports(scope, transports)
}
for _, transport := range transports {
name := "dns/" + transport.Type() + "[" + transport.Tag() + "]"
err := scope.Start(name, transport, stage)
if err != nil {
return err
}
}
return nil
}
func (m *TransportManager) startTransports(scope *adapter.Scope, transports []adapter.DNSTransport) error {
started := make(map[string]bool)
for {
canContinue := false
startOne:
for _, transportToStart := range transports {
transportTag := transportToStart.Tag()
if started[transportTag] {
continue
}
dependencies := transportToStart.Dependencies()
for _, dependency := range dependencies {
if !started[dependency] {
continue startOne
}
}
started[transportTag] = true
canContinue = true
name := "dns/" + transportToStart.Type() + "[" + transportTag + "]"
err := scope.Start(name, transportToStart, adapter.StartStateStart)
if err != nil {
return err
}
}
if len(started) == len(transports) {
break
}
if canContinue {
continue
}
currentTransport := common.Find(transports, func(it adapter.DNSTransport) bool {
return !started[it.Tag()]
})
var lintTransport func(oTree []string, oCurrent adapter.DNSTransport) error
lintTransport = func(oTree []string, oCurrent adapter.DNSTransport) error {
problemTransportTag := common.Find(oCurrent.Dependencies(), func(it string) bool {
return !started[it]
})
if common.Contains(oTree, problemTransportTag) {
return E.New("circular server dependency: ", strings.Join(oTree, " -> "), " -> ", problemTransportTag)
}
m.access.Lock()
problemTransport := m.transportByTag[problemTransportTag]
m.access.Unlock()
if problemTransport == nil {
return E.New("dependency[", problemTransportTag, "] not found for server[", oCurrent.Tag(), "]")
}
return lintTransport(append(oTree, problemTransportTag), problemTransport)
}
return lintTransport([]string{currentTransport.Tag()}, currentTransport)
}
return nil
}
func (m *TransportManager) Transports() []adapter.DNSTransport {
m.access.RLock()
defer m.access.RUnlock()
return m.transports
}
func (m *TransportManager) Transport(tag string) (adapter.DNSTransport, bool) {
m.access.RLock()
outbound, found := m.transportByTag[tag]
m.access.RUnlock()
return outbound, found
}
func (m *TransportManager) Default() adapter.DNSTransport {
m.access.RLock()
defer m.access.RUnlock()
return m.defaultTransport
}
func (m *TransportManager) FakeIP() adapter.FakeIPTransport {
m.access.RLock()
defer m.access.RUnlock()
return m.fakeIPTransport
}
func (m *TransportManager) Create(ctx context.Context, logger log.ContextLogger, tag string, transportType string, options any) error {
if tag == "" {
return os.ErrInvalid
}
transport, err := m.registry.CreateDNSTransport(ctx, logger, tag, transportType, options)
if err != nil {
return err
}
m.access.Lock()
defer m.access.Unlock()
_, loaded := m.transportByTag[tag]
if loaded {
return E.New("duplicate DNS server tag: ", tag)
}
m.transports = append(m.transports, transport)
m.transportByTag[tag] = transport
if tag == m.defaultTag || (m.defaultTag == "" && m.defaultTransport == nil) {
if transport.Type() == C.DNSTypeFakeIP {
return E.New("default server cannot be fakeip")
}
m.defaultTransport = transport
}
if transport.Type() == C.DNSTypeFakeIP {
if m.fakeIPTransport != nil {
return E.New("multiple fakeip server are not supported")
}
m.fakeIPTransport = transport.(adapter.FakeIPTransport)
}
return nil
}