diff --git a/cmd/internal/build_libbox/main.go b/cmd/internal/build_libbox/main.go index aa443b5d..44911797 100644 --- a/cmd/internal/build_libbox/main.go +++ b/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, ",")) diff --git a/experimental/libbox/dns.go b/experimental/libbox/dns.go new file mode 100644 index 00000000..bfb8dc53 --- /dev/null +++ b/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) +} diff --git a/experimental/libbox/setup.go b/experimental/libbox/setup.go index e9caf773..4afcecf2 100644 --- a/experimental/libbox/setup.go +++ b/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 +}