NekoBoxForAndroid/libcore/dns_box.go
2025-09-06 22:39:52 +09:00

167 lines
3.7 KiB
Go

// libbox/dns.go
package libcore
import (
"context"
"net/netip"
"strings"
"sync"
"syscall"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/dns"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/task"
mDNS "github.com/miekg/dns"
)
var rawQueryFunc func(networkHandle int64, request []byte) ([]byte, error)
type LocalDNSTransport interface {
Raw() bool
NetworkHandle() int64
Lookup(ctx *ExchangeContext, network string, domain string) error
Exchange(ctx *ExchangeContext, message []byte) error
}
var gLocalDNSTransport *platformLocalDNSTransport = nil
type platformLocalDNSTransport struct {
dns.TransportAdapter
iif LocalDNSTransport
raw bool
}
func newPlatformTransport(iif LocalDNSTransport, tag string, options option.LocalDNSServerOptions) *platformLocalDNSTransport {
return &platformLocalDNSTransport{
TransportAdapter: dns.NewTransportAdapterWithLocalOptions(constant.DNSTypeLocal, tag, options),
iif: iif,
raw: iif.Raw(),
}
}
func (p *platformLocalDNSTransport) Start(stage adapter.StartStage) error {
return nil
}
func (p *platformLocalDNSTransport) Close() error {
return nil
}
func (p *platformLocalDNSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
if p.raw && rawQueryFunc != nil {
// Raw - Android 10 及以上才有
messageBytes, err := message.Pack()
if err != nil {
return nil, err
}
msg, err := rawQueryFunc(p.iif.NetworkHandle(), messageBytes)
if err != nil {
return nil, err
}
responseMessage := new(mDNS.Msg)
err = responseMessage.Unpack(msg)
if err != nil {
return nil, err
}
return responseMessage, nil
} else {
// Lookup - Android 10 以下
question := message.Question[0]
var network string
switch question.Qtype {
case mDNS.TypeA:
network = "ip4"
case mDNS.TypeAAAA:
network = "ip6"
default:
return nil, E.New("only IP queries are supported by current version of Android")
}
done := make(chan struct{})
response := &ExchangeContext{
context: ctx,
done: sync.OnceFunc(func() {
close(done)
}),
}
var responseAddrs []netip.Addr
var group task.Group
group.Append0(func(ctx context.Context) error {
err := p.iif.Lookup(response, network, question.Name)
if err != nil {
return err
}
select {
case <-done:
case <-ctx.Done():
return context.Canceled
}
if response.error != nil {
return response.error
}
responseAddrs = response.addresses
return nil
})
err := group.Run(ctx)
if err != nil {
return nil, err
}
return dns.FixedResponse(message.Id, question, responseAddrs, constant.DefaultDNSTTL), nil
}
}
type Func interface {
Invoke() error
}
type ExchangeContext struct {
context context.Context
message mDNS.Msg
addresses []netip.Addr
error error
done func()
}
func (c *ExchangeContext) OnCancel(callback Func) {
go func() {
<-c.context.Done()
callback.Invoke()
}()
}
func (c *ExchangeContext) Success(result string) {
c.addresses = common.Map(common.Filter(strings.Split(result, "\n"), func(it string) bool {
return !common.IsEmpty(it)
}), func(it string) netip.Addr {
return M.ParseSocksaddrHostPort(it, 0).Unwrap().Addr
})
}
func (c *ExchangeContext) RawSuccess(result []byte) {
err := c.message.Unpack(result)
if err != nil {
c.error = E.Cause(err, "parse response")
}
c.done()
}
func (c *ExchangeContext) ErrorCode(code int32) {
c.error = dns.RcodeError(code)
c.done()
}
func (c *ExchangeContext) ErrnoCode(code int32) {
c.error = syscall.Errno(code)
c.done()
}