1
0
Эх сурвалжийг харах

platform: Improve `local` DNS transport

世界 2 жил өмнө
parent
commit
702d96a738

+ 4 - 2
cmd/internal/build_libbox/main.go

@@ -40,6 +40,7 @@ var (
 	sharedFlags []string
 	debugFlags  []string
 	sharedTags  []string
+	iosTags     []string
 	debugTags   []string
 )
 
@@ -53,7 +54,8 @@ func init() {
 	sharedFlags = append(sharedFlags, "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=")
 	debugFlags = append(debugFlags, "-X github.com/sagernet/sing-box/constant.Version="+currentTag)
 
-	sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_dhcp", "with_wireguard", "with_utls", "with_clash_api")
+	sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api")
+	iosTags = append(iosTags, "with_dhcp", "with_low_memory", "with_conntrack")
 	debugTags = append(debugTags, "debug")
 }
 
@@ -114,7 +116,7 @@ func buildiOS() {
 		args = append(args, debugFlags...)
 	}
 
-	tags := append(sharedTags, "with_low_memory", "with_conntrack")
+	tags := append(sharedTags, iosTags...)
 	args = append(args, "-tags")
 	if !debugEnabled {
 		args = append(args, strings.Join(tags, ","))

+ 158 - 0
experimental/libbox/dns.go

@@ -0,0 +1,158 @@
+package libbox
+
+import (
+	"context"
+	"net/netip"
+	"strings"
+	"syscall"
+
+	"github.com/sagernet/sing-dns"
+	"github.com/sagernet/sing/common"
+	E "github.com/sagernet/sing/common/exceptions"
+	"github.com/sagernet/sing/common/logger"
+	M "github.com/sagernet/sing/common/metadata"
+	N "github.com/sagernet/sing/common/network"
+	"github.com/sagernet/sing/common/task"
+
+	mDNS "github.com/miekg/dns"
+)
+
+type LocalDNSTransport interface {
+	Raw() bool
+	Lookup(ctx *ExchangeContext, network string, domain string) error
+	Exchange(ctx *ExchangeContext, message []byte) error
+}
+
+func RegisterLocalDNSTransport(transport LocalDNSTransport) {
+	if transport == nil {
+		dns.RegisterTransport([]string{"local"}, dns.CreateLocalTransport)
+	} else {
+		dns.RegisterTransport([]string{"local"}, func(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
+			return &platformLocalDNSTransport{
+				iif: transport,
+			}, nil
+		})
+	}
+}
+
+var _ dns.Transport = (*platformLocalDNSTransport)(nil)
+
+type platformLocalDNSTransport struct {
+	iif LocalDNSTransport
+}
+
+func (p *platformLocalDNSTransport) Name() string {
+	return "local"
+}
+
+func (p *platformLocalDNSTransport) Start() error {
+	return nil
+}
+
+func (p *platformLocalDNSTransport) Close() error {
+	return nil
+}
+
+func (p *platformLocalDNSTransport) Raw() bool {
+	return p.iif.Raw()
+}
+
+func (p *platformLocalDNSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
+	messageBytes, err := message.Pack()
+	if err != nil {
+		return nil, err
+	}
+	response := &ExchangeContext{
+		context: ctx,
+	}
+	var responseMessage *mDNS.Msg
+	return responseMessage, task.Run(ctx, func() error {
+		err = p.iif.Exchange(response, messageBytes)
+		if err != nil {
+			return err
+		}
+		if response.error != nil {
+			return response.error
+		}
+		responseMessage = &response.message
+		return nil
+	})
+}
+
+func (p *platformLocalDNSTransport) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) {
+	var network string
+	switch strategy {
+	case dns.DomainStrategyUseIPv4:
+		network = "ip4"
+	case dns.DomainStrategyPreferIPv6:
+		network = "ip6"
+	default:
+		network = "ip"
+	}
+	response := &ExchangeContext{
+		context: ctx,
+	}
+	var responseAddr []netip.Addr
+	return responseAddr, task.Run(ctx, func() error {
+		err := p.iif.Lookup(response, network, domain)
+		if err != nil {
+			return err
+		}
+		if response.error != nil {
+			return response.error
+		}
+		switch strategy {
+		case dns.DomainStrategyUseIPv4:
+			responseAddr = common.Filter(response.addresses, func(it netip.Addr) bool {
+				return it.Is4()
+			})
+		case dns.DomainStrategyPreferIPv6:
+			responseAddr = common.Filter(response.addresses, func(it netip.Addr) bool {
+				return it.Is6()
+			})
+		default:
+			responseAddr = response.addresses
+		}
+		/*if len(responseAddr) == 0 {
+			response.error = dns.RCodeSuccess
+		}*/
+		return nil
+	})
+}
+
+type ExchangeContext struct {
+	context   context.Context
+	message   mDNS.Msg
+	addresses []netip.Addr
+	error     error
+}
+
+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")
+	}
+}
+
+func (c *ExchangeContext) ErrorCode(code int32) {
+	c.error = dns.RCodeError(code)
+}
+
+func (c *ExchangeContext) Errno(errno int32) {
+	c.error = syscall.Errno(errno)
+}

+ 8 - 0
experimental/libbox/setup.go

@@ -35,3 +35,11 @@ func Version() string {
 func FormatBytes(length int64) string {
 	return humanize.IBytes(uint64(length))
 }
+
+type Func interface {
+	Invoke() error
+}
+
+type BoolFunc interface {
+	Invoke() bool
+}