github.com/sagernet/sing-box@v1.9.0-rc.20/experimental/libbox/dns.go (about) 1 package libbox 2 3 import ( 4 "context" 5 "net/netip" 6 "strings" 7 "syscall" 8 9 "github.com/sagernet/sing-dns" 10 "github.com/sagernet/sing/common" 11 E "github.com/sagernet/sing/common/exceptions" 12 M "github.com/sagernet/sing/common/metadata" 13 "github.com/sagernet/sing/common/task" 14 15 mDNS "github.com/miekg/dns" 16 ) 17 18 type LocalDNSTransport interface { 19 Raw() bool 20 Lookup(ctx *ExchangeContext, network string, domain string) error 21 Exchange(ctx *ExchangeContext, message []byte) error 22 } 23 24 func RegisterLocalDNSTransport(transport LocalDNSTransport) { 25 if transport == nil { 26 dns.RegisterTransport([]string{"local"}, func(options dns.TransportOptions) (dns.Transport, error) { 27 return dns.NewLocalTransport(options), nil 28 }) 29 } else { 30 dns.RegisterTransport([]string{"local"}, func(options dns.TransportOptions) (dns.Transport, error) { 31 return &platformLocalDNSTransport{ 32 iif: transport, 33 }, nil 34 }) 35 } 36 } 37 38 var _ dns.Transport = (*platformLocalDNSTransport)(nil) 39 40 type platformLocalDNSTransport struct { 41 iif LocalDNSTransport 42 } 43 44 func (p *platformLocalDNSTransport) Name() string { 45 return "local" 46 } 47 48 func (p *platformLocalDNSTransport) Start() error { 49 return nil 50 } 51 52 func (p *platformLocalDNSTransport) Reset() { 53 } 54 55 func (p *platformLocalDNSTransport) Close() error { 56 return nil 57 } 58 59 func (p *platformLocalDNSTransport) Raw() bool { 60 return p.iif.Raw() 61 } 62 63 func (p *platformLocalDNSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { 64 messageBytes, err := message.Pack() 65 if err != nil { 66 return nil, err 67 } 68 response := &ExchangeContext{ 69 context: ctx, 70 } 71 var responseMessage *mDNS.Msg 72 return responseMessage, task.Run(ctx, func() error { 73 err = p.iif.Exchange(response, messageBytes) 74 if err != nil { 75 return err 76 } 77 if response.error != nil { 78 return response.error 79 } 80 responseMessage = &response.message 81 return nil 82 }) 83 } 84 85 func (p *platformLocalDNSTransport) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) { 86 var network string 87 switch strategy { 88 case dns.DomainStrategyUseIPv4: 89 network = "ip4" 90 case dns.DomainStrategyPreferIPv6: 91 network = "ip6" 92 default: 93 network = "ip" 94 } 95 response := &ExchangeContext{ 96 context: ctx, 97 } 98 var responseAddr []netip.Addr 99 return responseAddr, task.Run(ctx, func() error { 100 err := p.iif.Lookup(response, network, domain) 101 if err != nil { 102 return err 103 } 104 if response.error != nil { 105 return response.error 106 } 107 switch strategy { 108 case dns.DomainStrategyUseIPv4: 109 responseAddr = common.Filter(response.addresses, func(it netip.Addr) bool { 110 return it.Is4() 111 }) 112 case dns.DomainStrategyPreferIPv6: 113 responseAddr = common.Filter(response.addresses, func(it netip.Addr) bool { 114 return it.Is6() 115 }) 116 default: 117 responseAddr = response.addresses 118 } 119 /*if len(responseAddr) == 0 { 120 response.error = dns.RCodeSuccess 121 }*/ 122 return nil 123 }) 124 } 125 126 type Func interface { 127 Invoke() error 128 } 129 130 type ExchangeContext struct { 131 context context.Context 132 message mDNS.Msg 133 addresses []netip.Addr 134 error error 135 } 136 137 func (c *ExchangeContext) OnCancel(callback Func) { 138 go func() { 139 <-c.context.Done() 140 callback.Invoke() 141 }() 142 } 143 144 func (c *ExchangeContext) Success(result string) { 145 c.addresses = common.Map(common.Filter(strings.Split(result, "\n"), func(it string) bool { 146 return !common.IsEmpty(it) 147 }), func(it string) netip.Addr { 148 return M.ParseSocksaddrHostPort(it, 0).Unwrap().Addr 149 }) 150 } 151 152 func (c *ExchangeContext) RawSuccess(result []byte) { 153 err := c.message.Unpack(result) 154 if err != nil { 155 c.error = E.Cause(err, "parse response") 156 } 157 } 158 159 func (c *ExchangeContext) ErrorCode(code int32) { 160 c.error = dns.RCodeError(code) 161 } 162 163 func (c *ExchangeContext) ErrnoCode(code int32) { 164 c.error = syscall.Errno(code) 165 }