From dc127e29941666d6cb095174d9c5769061211657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 11 Jul 2022 18:44:59 +0800 Subject: [PATCH] Migrate components to library --- .github/update_dependencies.sh | 2 + adapter/dns.go | 22 -- adapter/inbound.go | 4 +- adapter/router.go | 4 +- common/dialer/default.go | 4 +- common/dialer/dialer.go | 12 +- common/dialer/parallel.go | 90 -------- common/dialer/protect.go | 46 ---- common/dialer/protect_stub.go | 9 - common/dialer/resolve.go | 14 +- common/dialer/resolve_conn.go | 8 +- common/dialer/serial.go | 41 ---- common/domain/matcher.go | 60 ------ common/domain/matcher_test.go | 21 -- common/domain/set.go | 231 -------------------- common/tun/gvisor.go | 151 ------------- common/tun/gvisor_linux.go | 24 --- common/tun/gvisor_nonlinux.go | 9 - common/tun/gvisor_posix.go | 118 ---------- common/tun/tun.go | 12 -- common/tun/tun_linux.go | 113 ---------- common/tun/tun_other.go | 20 -- dns/client.go | 379 --------------------------------- dns/client_test.go | 33 --- dns/dialer.go | 49 ----- dns/rcode.go | 33 --- dns/transport.go | 50 ----- dns/transport_base.go | 45 ---- dns/transport_https.go | 94 -------- dns/transport_local.go | 79 ------- dns/transport_tcp.go | 183 ---------------- dns/transport_test.go | 93 -------- dns/transport_tls.go | 182 ---------------- dns/transport_udp.go | 160 -------------- format.go | 2 +- go.mod | 8 +- go.sum | 18 +- inbound/default.go | 7 +- inbound/tun.go | 9 +- option/dns.go | 11 +- option/types.go | 25 +-- outbound/default.go | 7 +- route/router.go | 37 ++-- route/rule_domain.go | 2 +- test/go.mod | 8 +- test/go.sum | 18 +- 46 files changed, 109 insertions(+), 2438 deletions(-) delete mode 100644 adapter/dns.go delete mode 100644 common/dialer/parallel.go delete mode 100644 common/dialer/protect.go delete mode 100644 common/dialer/protect_stub.go delete mode 100644 common/dialer/serial.go delete mode 100644 common/domain/matcher.go delete mode 100644 common/domain/matcher_test.go delete mode 100644 common/domain/set.go delete mode 100644 common/tun/gvisor.go delete mode 100644 common/tun/gvisor_linux.go delete mode 100644 common/tun/gvisor_nonlinux.go delete mode 100644 common/tun/gvisor_posix.go delete mode 100644 common/tun/tun.go delete mode 100644 common/tun/tun_linux.go delete mode 100644 common/tun/tun_other.go delete mode 100644 dns/client.go delete mode 100644 dns/client_test.go delete mode 100644 dns/dialer.go delete mode 100644 dns/rcode.go delete mode 100644 dns/transport.go delete mode 100644 dns/transport_base.go delete mode 100644 dns/transport_https.go delete mode 100644 dns/transport_local.go delete mode 100644 dns/transport_tcp.go delete mode 100644 dns/transport_test.go delete mode 100644 dns/transport_tls.go delete mode 100644 dns/transport_udp.go diff --git a/.github/update_dependencies.sh b/.github/update_dependencies.sh index 39593713..22edc6df 100755 --- a/.github/update_dependencies.sh +++ b/.github/update_dependencies.sh @@ -3,6 +3,8 @@ PROJECTS=$(dirname "$0")/../.. go get -x github.com/sagernet/sing@$(git -C $PROJECTS/sing rev-parse HEAD) +go get -x github.com/sagernet/sing-dns@$(git -C $PROJECTS/sing-dns rev-parse HEAD) +go get -x github.com/sagernet/sing-tun@$(git -C $PROJECTS/sing-dns rev-parse HEAD) go get -x github.com/sagernet/sing-shadowsocks@$(git -C $PROJECTS/sing-shadowsocks rev-parse HEAD) go mod tidy pushd test diff --git a/adapter/dns.go b/adapter/dns.go deleted file mode 100644 index a8a54167..00000000 --- a/adapter/dns.go +++ /dev/null @@ -1,22 +0,0 @@ -package adapter - -import ( - "context" - "net/netip" - - C "github.com/sagernet/sing-box/constant" - - "golang.org/x/net/dns/dnsmessage" -) - -type DNSClient interface { - Exchange(ctx context.Context, transport DNSTransport, message *dnsmessage.Message) (*dnsmessage.Message, error) - Lookup(ctx context.Context, transport DNSTransport, domain string, strategy C.DomainStrategy) ([]netip.Addr, error) -} - -type DNSTransport interface { - Service - Raw() bool - Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) - Lookup(ctx context.Context, domain string, strategy C.DomainStrategy) ([]netip.Addr, error) -} diff --git a/adapter/inbound.go b/adapter/inbound.go index 0e9aa12b..2532caed 100644 --- a/adapter/inbound.go +++ b/adapter/inbound.go @@ -4,7 +4,7 @@ import ( "context" "net/netip" - C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-dns" M "github.com/sagernet/sing/common/metadata" ) @@ -25,7 +25,7 @@ type InboundContext struct { // cache - DomainStrategy C.DomainStrategy + DomainStrategy dns.DomainStrategy SniffEnabled bool SniffOverrideDestination bool DestinationAddresses []netip.Addr diff --git a/adapter/router.go b/adapter/router.go index 052ad662..91236fed 100644 --- a/adapter/router.go +++ b/adapter/router.go @@ -6,7 +6,7 @@ import ( "net/netip" "github.com/sagernet/sing-box/common/geoip" - C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-dns" N "github.com/sagernet/sing/common/network" "golang.org/x/net/dns/dnsmessage" @@ -21,7 +21,7 @@ type Router interface { GeoIPReader() *geoip.Reader LoadGeosite(code string) (Rule, error) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) - Lookup(ctx context.Context, domain string, strategy C.DomainStrategy) ([]netip.Addr, error) + Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error) AutoDetectInterface() bool DefaultInterfaceName() string diff --git a/common/dialer/default.go b/common/dialer/default.go index 6f72309d..eb0567a5 100644 --- a/common/dialer/default.go +++ b/common/dialer/default.go @@ -37,8 +37,8 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia listener.Control = control.Append(listener.Control, control.ReuseAddr()) } if options.ProtectPath != "" { - dialer.Control = control.Append(dialer.Control, ProtectPath(options.ProtectPath)) - listener.Control = control.Append(listener.Control, ProtectPath(options.ProtectPath)) + dialer.Control = control.Append(dialer.Control, control.ProtectPath(options.ProtectPath)) + listener.Control = control.Append(listener.Control, control.ProtectPath(options.ProtectPath)) } if options.ConnectTimeout != 0 { dialer.Timeout = time.Duration(options.ConnectTimeout) diff --git a/common/dialer/dialer.go b/common/dialer/dialer.go index d6da5783..67cbff68 100644 --- a/common/dialer/dialer.go +++ b/common/dialer/dialer.go @@ -4,8 +4,8 @@ import ( "time" "github.com/sagernet/sing-box/adapter" - C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common" N "github.com/sagernet/sing/common/network" ) @@ -20,13 +20,9 @@ func New(router adapter.Router, options option.DialerOptions) N.Dialer { func NewOutbound(router adapter.Router, options option.OutboundDialerOptions) N.Dialer { dialer := New(router, options.DialerOptions) - domainStrategy := C.DomainStrategy(options.DomainStrategy) - if domainStrategy != C.DomainStrategyAsIS || options.Detour == "" { - fallbackDelay := time.Duration(options.FallbackDelay) - if fallbackDelay == 0 { - fallbackDelay = time.Millisecond * 300 - } - dialer = NewResolveDialer(router, dialer, domainStrategy, fallbackDelay) + domainStrategy := dns.DomainStrategy(options.DomainStrategy) + if domainStrategy != dns.DomainStrategyAsIS || options.Detour == "" { + dialer = NewResolveDialer(router, dialer, domainStrategy, time.Duration(options.FallbackDelay)) } if options.OverrideOptions.IsValid() { dialer = NewOverride(dialer, common.PtrValueOrDefault(options.OverrideOptions)) diff --git a/common/dialer/parallel.go b/common/dialer/parallel.go deleted file mode 100644 index d1c44ea3..00000000 --- a/common/dialer/parallel.go +++ /dev/null @@ -1,90 +0,0 @@ -package dialer - -import ( - "context" - "net" - "net/netip" - "time" - - C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing/common" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" -) - -func DialParallel(ctx context.Context, dialer N.Dialer, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy C.DomainStrategy, fallbackDelay time.Duration) (net.Conn, error) { - // kanged form net.Dial - - returned := make(chan struct{}) - defer close(returned) - - addresses4 := common.Filter(destinationAddresses, func(address netip.Addr) bool { - return address.Is4() || address.Is4In6() - }) - addresses6 := common.Filter(destinationAddresses, func(address netip.Addr) bool { - return address.Is6() && !address.Is4In6() - }) - if len(addresses4) == 0 || len(addresses6) == 0 { - return DialSerial(ctx, dialer, network, destination, destinationAddresses) - } - var primaries, fallbacks []netip.Addr - switch strategy { - case C.DomainStrategyPreferIPv6: - primaries = addresses6 - fallbacks = addresses4 - default: - primaries = addresses4 - fallbacks = addresses6 - } - type dialResult struct { - net.Conn - error - primary bool - done bool - } - results := make(chan dialResult) // unbuffered - startRacer := func(ctx context.Context, primary bool) { - ras := primaries - if !primary { - ras = fallbacks - } - c, err := DialSerial(ctx, dialer, network, destination, ras) - select { - case results <- dialResult{Conn: c, error: err, primary: primary, done: true}: - case <-returned: - if c != nil { - c.Close() - } - } - } - var primary, fallback dialResult - primaryCtx, primaryCancel := context.WithCancel(ctx) - defer primaryCancel() - go startRacer(primaryCtx, true) - fallbackTimer := time.NewTimer(fallbackDelay) - defer fallbackTimer.Stop() - for { - select { - case <-fallbackTimer.C: - fallbackCtx, fallbackCancel := context.WithCancel(ctx) - defer fallbackCancel() - go startRacer(fallbackCtx, false) - - case res := <-results: - if res.error == nil { - return res.Conn, nil - } - if res.primary { - primary = res - } else { - fallback = res - } - if primary.done && fallback.done { - return nil, primary.error - } - if res.primary && fallbackTimer.Stop() { - fallbackTimer.Reset(0) - } - } - } -} diff --git a/common/dialer/protect.go b/common/dialer/protect.go deleted file mode 100644 index 9fd05d21..00000000 --- a/common/dialer/protect.go +++ /dev/null @@ -1,46 +0,0 @@ -//go:build android || with_protect - -package dialer - -import ( - "syscall" - - "github.com/sagernet/sing/common/control" - E "github.com/sagernet/sing/common/exceptions" -) - -func sendAncillaryFileDescriptors(protectPath string, fileDescriptors []int) error { - socket, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM, 0) - if err != nil { - return E.Cause(err, "open protect socket") - } - defer syscall.Close(socket) - err = syscall.Connect(socket, &syscall.SockaddrUnix{Name: protectPath}) - if err != nil { - return E.Cause(err, "connect protect path") - } - oob := syscall.UnixRights(fileDescriptors...) - dummy := []byte{1} - err = syscall.Sendmsg(socket, dummy, oob, nil, 0) - if err != nil { - return err - } - n, err := syscall.Read(socket, dummy) - if err != nil { - return err - } - if n != 1 { - return E.New("failed to protect fd") - } - return nil -} - -func ProtectPath(protectPath string) control.Func { - return func(network, address string, conn syscall.RawConn) error { - var innerErr error - err := conn.Control(func(fd uintptr) { - innerErr = sendAncillaryFileDescriptors(protectPath, []int{int(fd)}) - }) - return E.Errors(innerErr, err) - } -} diff --git a/common/dialer/protect_stub.go b/common/dialer/protect_stub.go deleted file mode 100644 index b484654a..00000000 --- a/common/dialer/protect_stub.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build !android && !with_protect - -package dialer - -import "github.com/sagernet/sing/common/control" - -func ProtectPath(protectPath string) control.Func { - return nil -} diff --git a/common/dialer/resolve.go b/common/dialer/resolve.go index 1c258ac0..50334166 100644 --- a/common/dialer/resolve.go +++ b/common/dialer/resolve.go @@ -7,7 +7,7 @@ import ( "time" "github.com/sagernet/sing-box/adapter" - C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-dns" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) @@ -15,11 +15,11 @@ import ( type ResolveDialer struct { dialer N.Dialer router adapter.Router - strategy C.DomainStrategy + strategy dns.DomainStrategy fallbackDelay time.Duration } -func NewResolveDialer(router adapter.Router, dialer N.Dialer, strategy C.DomainStrategy, fallbackDelay time.Duration) *ResolveDialer { +func NewResolveDialer(router adapter.Router, dialer N.Dialer, strategy dns.DomainStrategy, fallbackDelay time.Duration) *ResolveDialer { return &ResolveDialer{ dialer, router, @@ -37,7 +37,7 @@ func (d *ResolveDialer) DialContext(ctx context.Context, network string, destina metadata.Domain = "" var addresses []netip.Addr var err error - if d.strategy == C.DomainStrategyAsIS { + if d.strategy == dns.DomainStrategyAsIS { addresses, err = d.router.LookupDefault(ctx, destination.Fqdn) } else { addresses, err = d.router.Lookup(ctx, destination.Fqdn, d.strategy) @@ -45,7 +45,7 @@ func (d *ResolveDialer) DialContext(ctx context.Context, network string, destina if err != nil { return nil, err } - return DialParallel(ctx, d.dialer, network, destination, addresses, d.strategy, d.fallbackDelay) + return N.DialParallel(ctx, d.dialer, network, destination, addresses, d.strategy == dns.DomainStrategyPreferIPv6, d.fallbackDelay) } func (d *ResolveDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { @@ -57,7 +57,7 @@ func (d *ResolveDialer) ListenPacket(ctx context.Context, destination M.Socksadd metadata.Domain = "" var addresses []netip.Addr var err error - if d.strategy == C.DomainStrategyAsIS { + if d.strategy == dns.DomainStrategyAsIS { addresses, err = d.router.LookupDefault(ctx, destination.Fqdn) } else { addresses, err = d.router.Lookup(ctx, destination.Fqdn, d.strategy) @@ -65,7 +65,7 @@ func (d *ResolveDialer) ListenPacket(ctx context.Context, destination M.Socksadd if err != nil { return nil, err } - conn, err := ListenSerial(ctx, d.dialer, destination, addresses) + conn, err := N.ListenSerial(ctx, d.dialer, destination, addresses) if err != nil { return nil, err } diff --git a/common/dialer/resolve_conn.go b/common/dialer/resolve_conn.go index 10ae32d5..8e9b3b22 100644 --- a/common/dialer/resolve_conn.go +++ b/common/dialer/resolve_conn.go @@ -5,14 +5,14 @@ import ( "net" "github.com/sagernet/sing-box/adapter" - C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) -func NewResolvePacketConn(router adapter.Router, strategy C.DomainStrategy, conn net.PacketConn) N.NetPacketConn { +func NewResolvePacketConn(router adapter.Router, strategy dns.DomainStrategy, conn net.PacketConn) N.NetPacketConn { if udpConn, ok := conn.(*net.UDPConn); ok { return &ResolveUDPConn{udpConn, router, strategy} } else { @@ -23,7 +23,7 @@ func NewResolvePacketConn(router adapter.Router, strategy C.DomainStrategy, conn type ResolveUDPConn struct { *net.UDPConn router adapter.Router - strategy C.DomainStrategy + strategy dns.DomainStrategy } func (w *ResolveUDPConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) { @@ -54,7 +54,7 @@ func (w *ResolveUDPConn) Upstream() any { type ResolvePacketConn struct { net.PacketConn router adapter.Router - strategy C.DomainStrategy + strategy dns.DomainStrategy } func (w *ResolvePacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) { diff --git a/common/dialer/serial.go b/common/dialer/serial.go deleted file mode 100644 index 6fdaa389..00000000 --- a/common/dialer/serial.go +++ /dev/null @@ -1,41 +0,0 @@ -package dialer - -import ( - "context" - "net" - "net/netip" - - E "github.com/sagernet/sing/common/exceptions" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" -) - -func DialSerial(ctx context.Context, dialer N.Dialer, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) { - var conn net.Conn - var err error - var connErrors []error - for _, address := range destinationAddresses { - conn, err = dialer.DialContext(ctx, network, M.SocksaddrFromAddrPort(address, destination.Port)) - if err != nil { - connErrors = append(connErrors, err) - continue - } - return conn, nil - } - return nil, E.Errors(connErrors...) -} - -func ListenSerial(ctx context.Context, dialer N.Dialer, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.PacketConn, error) { - var conn net.PacketConn - var err error - var connErrors []error - for _, address := range destinationAddresses { - conn, err = dialer.ListenPacket(ctx, M.SocksaddrFromAddrPort(address, destination.Port)) - if err != nil { - connErrors = append(connErrors, err) - continue - } - return conn, nil - } - return nil, E.Errors(connErrors...) -} diff --git a/common/domain/matcher.go b/common/domain/matcher.go deleted file mode 100644 index 95dc0dc8..00000000 --- a/common/domain/matcher.go +++ /dev/null @@ -1,60 +0,0 @@ -package domain - -import ( - "sort" - "unicode/utf8" -) - -type Matcher struct { - set *succinctSet -} - -func NewMatcher(domains []string, domainSuffix []string) *Matcher { - domainList := make([]string, 0, len(domains)+len(domainSuffix)) - seen := make(map[string]bool, len(domainList)) - for _, domain := range domainSuffix { - if seen[domain] { - continue - } - seen[domain] = true - domainList = append(domainList, reverseDomainSuffix(domain)) - } - for _, domain := range domains { - if seen[domain] { - continue - } - seen[domain] = true - domainList = append(domainList, reverseDomain(domain)) - } - sort.Strings(domainList) - return &Matcher{ - newSuccinctSet(domainList), - } -} - -func (m *Matcher) Match(domain string) bool { - return m.set.Has(reverseDomain(domain)) -} - -func reverseDomain(domain string) string { - l := len(domain) - b := make([]byte, l) - for i := 0; i < l; { - r, n := utf8.DecodeRuneInString(domain[i:]) - i += n - utf8.EncodeRune(b[l-i:], r) - } - return string(b) -} - -func reverseDomainSuffix(domain string) string { - l := len(domain) - b := make([]byte, l+1) - for i := 0; i < l; { - r, n := utf8.DecodeRuneInString(domain[i:]) - i += n - utf8.EncodeRune(b[l-i:], r) - } - b[l] = prefixLabel - return string(b) -} diff --git a/common/domain/matcher_test.go b/common/domain/matcher_test.go deleted file mode 100644 index 93e4bde3..00000000 --- a/common/domain/matcher_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package domain_test - -import ( - "testing" - - "github.com/sagernet/sing-box/common/domain" - - "github.com/stretchr/testify/require" -) - -func TestMatch(t *testing.T) { - t.Parallel() - r := require.New(t) - matcher := domain.NewMatcher([]string{"domain.com"}, []string{"suffix.com", ".suffix.org"}) - r.True(matcher.Match("domain.com")) - r.False(matcher.Match("my.domain.com")) - r.True(matcher.Match("suffix.com")) - r.True(matcher.Match("my.suffix.com")) - r.False(matcher.Match("suffix.org")) - r.True(matcher.Match("my.suffix.org")) -} diff --git a/common/domain/set.go b/common/domain/set.go deleted file mode 100644 index adf661ac..00000000 --- a/common/domain/set.go +++ /dev/null @@ -1,231 +0,0 @@ -package domain - -import ( - "math/bits" -) - -const prefixLabel = '\r' - -// mod from https://github.com/openacid/succinct - -type succinctSet struct { - leaves, labelBitmap []uint64 - labels []byte - ranks, selects []int32 -} - -func newSuccinctSet(keys []string) *succinctSet { - ss := &succinctSet{} - lIdx := 0 - type qElt struct{ s, e, col int } - queue := []qElt{{0, len(keys), 0}} - for i := 0; i < len(queue); i++ { - elt := queue[i] - if elt.col == len(keys[elt.s]) { - // a leaf node - elt.s++ - setBit(&ss.leaves, i, 1) - } - for j := elt.s; j < elt.e; { - frm := j - for ; j < elt.e && keys[j][elt.col] == keys[frm][elt.col]; j++ { - } - queue = append(queue, qElt{frm, j, elt.col + 1}) - ss.labels = append(ss.labels, keys[frm][elt.col]) - setBit(&ss.labelBitmap, lIdx, 0) - lIdx++ - } - setBit(&ss.labelBitmap, lIdx, 1) - lIdx++ - } - ss.init() - return ss -} - -func (ss *succinctSet) Has(key string) bool { - var nodeId, bmIdx int - for i := 0; i < len(key); i++ { - currentChar := key[i] - for ; ; bmIdx++ { - if getBit(ss.labelBitmap, bmIdx) != 0 { - return false - } - nextLabel := ss.labels[bmIdx-nodeId] - if nextLabel == prefixLabel { - return true - } - if nextLabel == currentChar { - break - } - } - nodeId = countZeros(ss.labelBitmap, ss.ranks, bmIdx+1) - bmIdx = selectIthOne(ss.labelBitmap, ss.ranks, ss.selects, nodeId-1) + 1 - } - if getBit(ss.leaves, nodeId) != 0 { - return true - } - for ; ; bmIdx++ { - if getBit(ss.labelBitmap, bmIdx) != 0 { - return false - } - if ss.labels[bmIdx-nodeId] == prefixLabel { - return true - } - } -} - -func setBit(bm *[]uint64, i int, v int) { - for i>>6 >= len(*bm) { - *bm = append(*bm, 0) - } - (*bm)[i>>6] |= uint64(v) << uint(i&63) -} - -func getBit(bm []uint64, i int) uint64 { - return bm[i>>6] & (1 << uint(i&63)) -} - -func (ss *succinctSet) init() { - ss.selects, ss.ranks = indexSelect32R64(ss.labelBitmap) -} - -func countZeros(bm []uint64, ranks []int32, i int) int { - a, _ := rank64(bm, ranks, int32(i)) - return i - int(a) -} - -func selectIthOne(bm []uint64, ranks, selects []int32, i int) int { - a, _ := select32R64(bm, selects, ranks, int32(i)) - return int(a) -} - -func rank64(words []uint64, rindex []int32, i int32) (int32, int32) { - wordI := i >> 6 - j := uint32(i & 63) - n := rindex[wordI] - w := words[wordI] - c1 := n + int32(bits.OnesCount64(w&mask[j])) - return c1, int32(w>>uint(j)) & 1 -} - -func indexRank64(words []uint64, opts ...bool) []int32 { - trailing := false - if len(opts) > 0 { - trailing = opts[0] - } - l := len(words) - if trailing { - l++ - } - idx := make([]int32, l) - n := int32(0) - for i := 0; i < len(words); i++ { - idx[i] = n - n += int32(bits.OnesCount64(words[i])) - } - if trailing { - idx[len(words)] = n - } - return idx -} - -func select32R64(words []uint64, selectIndex, rankIndex []int32, i int32) (int32, int32) { - a := int32(0) - l := int32(len(words)) - wordI := selectIndex[i>>5] >> 6 - for ; rankIndex[wordI+1] <= i; wordI++ { - } - w := words[wordI] - ww := w - base := wordI << 6 - findIth := int(i - rankIndex[wordI]) - offset := int32(0) - ones := bits.OnesCount32(uint32(ww)) - if ones <= findIth { - findIth -= ones - offset |= 32 - ww >>= 32 - } - ones = bits.OnesCount16(uint16(ww)) - if ones <= findIth { - findIth -= ones - offset |= 16 - ww >>= 16 - } - ones = bits.OnesCount8(uint8(ww)) - if ones <= findIth { - a = int32(select8Lookup[(ww>>5)&(0x7f8)|uint64(findIth-ones)]) + offset + 8 - } else { - a = int32(select8Lookup[(ww&0xff)<<3|uint64(findIth)]) + offset - } - a += base - w &= rMaskUpto[a&63] - if w != 0 { - return a, base + int32(bits.TrailingZeros64(w)) - } - wordI++ - for ; wordI < l; wordI++ { - w = words[wordI] - if w != 0 { - return a, wordI<<6 + int32(bits.TrailingZeros64(w)) - } - } - return a, l << 6 -} - -func indexSelect32R64(words []uint64) ([]int32, []int32) { - l := len(words) << 6 - sidx := make([]int32, 0, len(words)) - - ith := -1 - for i := 0; i < l; i++ { - if words[i>>6]&(1< 0 || len(response6) > 0 { - return sortAddresses(response4, response6, strategy), nil - } - } - } - var rCode dnsmessage.RCode - response, err := transport.Lookup(ctx, domain, strategy) - if err != nil { - err = wrapError(err) - if rCodeError, isRCodeError := err.(RCodeError); !isRCodeError { - return nil, err - } else { - rCode = dnsmessage.RCode(rCodeError) - } - if c.disableCache { - return nil, err - } - } - header := dnsmessage.Header{ - Response: true, - Authoritative: true, - RCode: rCode, - } - if !c.disableCache { - if strategy != C.DomainStrategyUseIPv6 { - question4 := dnsmessage.Question{ - Name: dnsName, - Type: dnsmessage.TypeA, - Class: dnsmessage.ClassINET, - } - response4 := common.Filter(response, func(addr netip.Addr) bool { - return addr.Is4() || addr.Is4In6() - }) - message4 := &dnsmessage.Message{ - Header: header, - Questions: []dnsmessage.Question{question4}, - } - if len(response4) > 0 { - for _, address := range response4 { - message4.Answers = append(message4.Answers, dnsmessage.Resource{ - Header: dnsmessage.ResourceHeader{ - Name: question4.Name, - Class: question4.Class, - TTL: DefaultTTL, - }, - Body: &dnsmessage.AResource{ - A: address.As4(), - }, - }) - } - } - c.storeCache(question4, message4) - } - if strategy != C.DomainStrategyUseIPv4 { - question6 := dnsmessage.Question{ - Name: dnsName, - Type: dnsmessage.TypeAAAA, - Class: dnsmessage.ClassINET, - } - response6 := common.Filter(response, func(addr netip.Addr) bool { - return addr.Is6() && !addr.Is4In6() - }) - message6 := &dnsmessage.Message{ - Header: header, - Questions: []dnsmessage.Question{question6}, - } - if len(response6) > 0 { - for _, address := range response6 { - message6.Answers = append(message6.Answers, dnsmessage.Resource{ - Header: dnsmessage.ResourceHeader{ - Name: question6.Name, - Class: question6.Class, - TTL: DefaultTTL, - }, - Body: &dnsmessage.AAAAResource{ - AAAA: address.As16(), - }, - }) - } - } - c.storeCache(question6, message6) - } - } - return response, err -} - -func sortAddresses(response4 []netip.Addr, response6 []netip.Addr, strategy C.DomainStrategy) []netip.Addr { - if strategy == C.DomainStrategyPreferIPv6 { - return append(response6, response4...) - } else { - return append(response4, response6...) - } -} - -func (c *Client) storeCache(question dnsmessage.Question, message *dnsmessage.Message) { - if c.disableExpire { - c.cache.Store(question, message) - return - } - timeToLive := DefaultTTL - for _, answer := range message.Answers { - if int(answer.Header.TTL) < timeToLive { - timeToLive = int(answer.Header.TTL) - } - } - expire := time.Now().Add(time.Second * time.Duration(timeToLive)) - c.cache.StoreWithExpire(question, message, expire) -} - -func (c *Client) exchangeToLookup(ctx context.Context, transport adapter.DNSTransport, message *dnsmessage.Message, question dnsmessage.Question) (*dnsmessage.Message, error) { - domain := question.Name.String() - var strategy C.DomainStrategy - if question.Type == dnsmessage.TypeA { - strategy = C.DomainStrategyUseIPv4 - } else { - strategy = C.DomainStrategyUseIPv6 - } - var rCode dnsmessage.RCode - result, err := c.Lookup(ctx, transport, domain, strategy) - if err != nil { - err = wrapError(err) - if rCodeError, isRCodeError := err.(RCodeError); !isRCodeError { - return nil, err - } else { - rCode = dnsmessage.RCode(rCodeError) - } - } - response := dnsmessage.Message{ - Header: dnsmessage.Header{ - ID: message.ID, - RCode: rCode, - RecursionAvailable: true, - RecursionDesired: true, - Response: true, - }, - Questions: message.Questions, - } - for _, address := range result { - var resource dnsmessage.Resource - resource.Header = dnsmessage.ResourceHeader{ - Name: question.Name, - Class: question.Class, - TTL: DefaultTTL, - } - if address.Is4() || address.Is4In6() { - resource.Body = &dnsmessage.AResource{ - A: address.As4(), - } - } else { - resource.Body = &dnsmessage.AAAAResource{ - AAAA: address.As16(), - } - } - } - return &response, nil -} - -func (c *Client) lookupToExchange(ctx context.Context, transport adapter.DNSTransport, name dnsmessage.Name, qType dnsmessage.Type) ([]netip.Addr, error) { - question := dnsmessage.Question{ - Name: name, - Type: qType, - Class: dnsmessage.ClassINET, - } - if !c.disableCache { - cachedAddresses, err := c.questionCache(question) - if err != ErrNotCached { - return cachedAddresses, err - } - } - message := dnsmessage.Message{ - Header: dnsmessage.Header{ - ID: 0, - RecursionDesired: true, - }, - Questions: []dnsmessage.Question{question}, - } - response, err := c.Exchange(ctx, transport, &message) - if err != nil { - return nil, err - } - return messageToAddresses(response) -} - -func (c *Client) questionCache(question dnsmessage.Question) ([]netip.Addr, error) { - response, cached := c.cache.Load(question) - if !cached { - return nil, ErrNotCached - } - return messageToAddresses(response) -} - -func messageToAddresses(response *dnsmessage.Message) ([]netip.Addr, error) { - if response.RCode != dnsmessage.RCodeSuccess { - return nil, RCodeError(response.RCode) - } - addresses := make([]netip.Addr, 0, len(response.Answers)) - for _, answer := range response.Answers { - switch resource := answer.Body.(type) { - case *dnsmessage.AResource: - addresses = append(addresses, netip.AddrFrom4(resource.A)) - case *dnsmessage.AAAAResource: - addresses = append(addresses, netip.AddrFrom16(resource.AAAA)) - } - } - return addresses, nil -} - -func wrapError(err error) error { - if dnsErr, isDNSError := err.(*net.DNSError); isDNSError { - if dnsErr.IsNotFound { - return RCodeNameError - } - } - return err -} diff --git a/dns/client_test.go b/dns/client_test.go deleted file mode 100644 index 074971bf..00000000 --- a/dns/client_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package dns_test - -import ( - "context" - "testing" - "time" - - C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/dns" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing-box/option" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" - - "github.com/stretchr/testify/require" -) - -func TestClient(t *testing.T) { - t.Parallel() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - client := dns.NewClient(option.DNSClientOptions{}) - dnsTransport := dns.NewTCPTransport(context.Background(), N.SystemDialer, log.NewNopLogger(), M.ParseSocksaddr("1.0.0.1:53")) - response, err := client.Exchange(ctx, dnsTransport, makeQuery()) - require.NoError(t, err) - require.NotEmpty(t, response.Answers, "no answers") - response, err = client.Exchange(ctx, dnsTransport, makeQuery()) - require.NoError(t, err) - require.NotEmpty(t, response.Answers, "no answers") - addresses, err := client.Lookup(ctx, dnsTransport, "www.google.com", C.DomainStrategyAsIS) - require.NoError(t, err) - require.NotEmpty(t, addresses, "no answers") - cancel() -} diff --git a/dns/dialer.go b/dns/dialer.go deleted file mode 100644 index efb663eb..00000000 --- a/dns/dialer.go +++ /dev/null @@ -1,49 +0,0 @@ -package dns - -import ( - "context" - "net" - - "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/common/dialer" - C "github.com/sagernet/sing-box/constant" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" -) - -type DialerWrapper struct { - dialer N.Dialer - strategy C.DomainStrategy - client adapter.DNSClient - transport adapter.DNSTransport -} - -func NewDialerWrapper(dialer N.Dialer, strategy C.DomainStrategy, client adapter.DNSClient, transport adapter.DNSTransport) N.Dialer { - return &DialerWrapper{dialer, strategy, client, transport} -} - -func (d *DialerWrapper) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - if destination.IsIP() { - return d.dialer.DialContext(ctx, network, destination) - } - addresses, err := d.client.Lookup(ctx, d.transport, destination.Fqdn, d.strategy) - if err != nil { - return nil, err - } - return dialer.DialSerial(ctx, d.dialer, network, destination, addresses) -} - -func (d *DialerWrapper) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - if destination.IsIP() { - return d.dialer.ListenPacket(ctx, destination) - } - addresses, err := d.client.Lookup(ctx, d.transport, destination.Fqdn, d.strategy) - if err != nil { - return nil, err - } - return dialer.ListenSerial(ctx, d.dialer, destination, addresses) -} - -func (d *DialerWrapper) Upstream() any { - return d.dialer -} diff --git a/dns/rcode.go b/dns/rcode.go deleted file mode 100644 index 5b7e52cc..00000000 --- a/dns/rcode.go +++ /dev/null @@ -1,33 +0,0 @@ -package dns - -import F "github.com/sagernet/sing/common/format" - -const ( - RCodeSuccess RCodeError = 0 // NoError - RCodeFormatError RCodeError = 1 // FormErr - RCodeServerFailure RCodeError = 2 // ServFail - RCodeNameError RCodeError = 3 // NXDomain - RCodeNotImplemented RCodeError = 4 // NotImp - RCodeRefused RCodeError = 5 // Refused -) - -type RCodeError uint16 - -func (e RCodeError) Error() string { - switch e { - case RCodeSuccess: - return "success" - case RCodeFormatError: - return "format error" - case RCodeServerFailure: - return "server failure" - case RCodeNameError: - return "name error" - case RCodeNotImplemented: - return "not implemented" - case RCodeRefused: - return "refused" - default: - return F.ToString("unknown error: ", uint16(e)) - } -} diff --git a/dns/transport.go b/dns/transport.go deleted file mode 100644 index e3137072..00000000 --- a/dns/transport.go +++ /dev/null @@ -1,50 +0,0 @@ -package dns - -import ( - "context" - "net/url" - - "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/log" - E "github.com/sagernet/sing/common/exceptions" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" -) - -func NewTransport(ctx context.Context, dialer N.Dialer, logger log.Logger, address string) (adapter.DNSTransport, error) { - if address == "local" { - return NewLocalTransport(), nil - } - serverURL, err := url.Parse(address) - if err != nil { - return nil, err - } - host := serverURL.Hostname() - if host == "" { - host = address - } - port := serverURL.Port() - switch serverURL.Scheme { - case "tls": - if port == "" { - port = "853" - } - default: - if port == "" { - port = "53" - } - } - destination := M.ParseSocksaddrHostPortStr(host, port) - switch serverURL.Scheme { - case "", "udp": - return NewUDPTransport(ctx, dialer, logger, destination), nil - case "tcp": - return NewTCPTransport(ctx, dialer, logger, destination), nil - case "tls": - return NewTLSTransport(ctx, dialer, logger, destination), nil - case "https": - return NewHTTPSTransport(dialer, serverURL.String()), nil - default: - return nil, E.New("unknown dns scheme: " + serverURL.Scheme) - } -} diff --git a/dns/transport_base.go b/dns/transport_base.go deleted file mode 100644 index dbc20f85..00000000 --- a/dns/transport_base.go +++ /dev/null @@ -1,45 +0,0 @@ -package dns - -import ( - "context" - "net/netip" - "os" - "sync" - - C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/log" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" -) - -type myTransportAdapter struct { - ctx context.Context - dialer N.Dialer - logger log.Logger - destination M.Socksaddr - done chan struct{} - access sync.RWMutex - connection *dnsConnection -} - -func (t *myTransportAdapter) Start() error { - return nil -} - -func (t *myTransportAdapter) Close() error { - select { - case <-t.done: - return os.ErrClosed - default: - } - close(t.done) - return nil -} - -func (t *myTransportAdapter) Raw() bool { - return true -} - -func (t *myTransportAdapter) Lookup(ctx context.Context, domain string, strategy C.DomainStrategy) ([]netip.Addr, error) { - return nil, os.ErrInvalid -} diff --git a/dns/transport_https.go b/dns/transport_https.go deleted file mode 100644 index 45c3b874..00000000 --- a/dns/transport_https.go +++ /dev/null @@ -1,94 +0,0 @@ -package dns - -import ( - "bytes" - "context" - "net" - "net/http" - "net/netip" - "os" - - "github.com/sagernet/sing-box/adapter" - C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing/common" - "github.com/sagernet/sing/common/buf" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" - - "golang.org/x/net/dns/dnsmessage" -) - -const dnsMimeType = "application/dns-message" - -var _ adapter.DNSTransport = (*HTTPSTransport)(nil) - -type HTTPSTransport struct { - destination string - transport *http.Transport -} - -func NewHTTPSTransport(dialer N.Dialer, destination string) *HTTPSTransport { - return &HTTPSTransport{ - destination: destination, - transport: &http.Transport{ - ForceAttemptHTTP2: true, - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr)) - }, - }, - } -} - -func (t *HTTPSTransport) Start() error { - return nil -} - -func (t *HTTPSTransport) Close() error { - t.transport.CloseIdleConnections() - return nil -} - -func (t *HTTPSTransport) Raw() bool { - return true -} - -func (t *HTTPSTransport) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) { - message.ID = 0 - _buffer := buf.StackNewSize(1024) - defer common.KeepAlive(_buffer) - buffer := common.Dup(_buffer) - defer buffer.Release() - rawMessage, err := message.AppendPack(buffer.Index(0)) - if err != nil { - return nil, err - } - buffer.Truncate(len(rawMessage)) - request, err := http.NewRequestWithContext(ctx, http.MethodPost, t.destination, bytes.NewReader(buffer.Bytes())) - if err != nil { - return nil, err - } - request.Header.Set("content-type", dnsMimeType) - request.Header.Set("accept", dnsMimeType) - - client := &http.Client{Transport: t.transport} - response, err := client.Do(request) - if err != nil { - return nil, err - } - defer response.Body.Close() - buffer.FullReset() - _, err = buffer.ReadAllFrom(response.Body) - if err != nil { - return nil, err - } - var responseMessage dnsmessage.Message - err = responseMessage.Unpack(buffer.Bytes()) - if err != nil { - return nil, err - } - return &responseMessage, nil -} - -func (t *HTTPSTransport) Lookup(ctx context.Context, domain string, strategy C.DomainStrategy) ([]netip.Addr, error) { - return nil, os.ErrInvalid -} diff --git a/dns/transport_local.go b/dns/transport_local.go deleted file mode 100644 index a7044160..00000000 --- a/dns/transport_local.go +++ /dev/null @@ -1,79 +0,0 @@ -package dns - -import ( - "context" - "net" - "net/netip" - "os" - "sort" - - "github.com/sagernet/sing-box/adapter" - C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing/common" - - "golang.org/x/net/dns/dnsmessage" -) - -var LocalTransportConstructor func() adapter.DNSTransport - -func NewLocalTransport() adapter.DNSTransport { - if LocalTransportConstructor != nil { - return LocalTransportConstructor() - } - return &LocalTransport{} -} - -var _ adapter.DNSTransport = (*LocalTransport)(nil) - -type LocalTransport struct { - resolver net.Resolver -} - -func (t *LocalTransport) Start() error { - return nil -} - -func (t *LocalTransport) Close() error { - return nil -} - -func (t *LocalTransport) Raw() bool { - return false -} - -func (t *LocalTransport) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) { - return nil, os.ErrInvalid -} - -func (t *LocalTransport) Lookup(ctx context.Context, domain string, strategy C.DomainStrategy) ([]netip.Addr, error) { - var network string - switch strategy { - case C.DomainStrategyAsIS, C.DomainStrategyPreferIPv4, C.DomainStrategyPreferIPv6: - network = "ip" - case C.DomainStrategyUseIPv4: - network = "ip4" - case C.DomainStrategyUseIPv6: - network = "ip6" - } - addrs, err := t.resolver.LookupNetIP(ctx, network, domain) - if err != nil { - return nil, err - } - addrs = common.Map(addrs, func(it netip.Addr) netip.Addr { - if it.Is4In6() { - return netip.AddrFrom4(it.As4()) - } - return it - }) - switch strategy { - case C.DomainStrategyPreferIPv4: - sort.Slice(addrs, func(i, j int) bool { - return addrs[i].Is4() && addrs[j].Is6() - }) - case C.DomainStrategyPreferIPv6: - sort.Slice(addrs, func(i, j int) bool { - return addrs[i].Is6() && addrs[j].Is4() - }) - } - return addrs, nil -} diff --git a/dns/transport_tcp.go b/dns/transport_tcp.go deleted file mode 100644 index a97ac9e8..00000000 --- a/dns/transport_tcp.go +++ /dev/null @@ -1,183 +0,0 @@ -package dns - -import ( - "context" - "encoding/binary" - "net" - "os" - "sync" - - "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing/common" - "github.com/sagernet/sing/common/buf" - E "github.com/sagernet/sing/common/exceptions" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" - "github.com/sagernet/sing/common/task" - - "golang.org/x/net/dns/dnsmessage" -) - -var _ adapter.DNSTransport = (*TCPTransport)(nil) - -type TCPTransport struct { - myTransportAdapter -} - -func NewTCPTransport(ctx context.Context, dialer N.Dialer, logger log.Logger, destination M.Socksaddr) *TCPTransport { - return &TCPTransport{ - myTransportAdapter{ - ctx: ctx, - dialer: dialer, - logger: logger, - destination: destination, - done: make(chan struct{}), - }, - } -} - -func (t *TCPTransport) offer() (*dnsConnection, error) { - t.access.RLock() - connection := t.connection - t.access.RUnlock() - if connection != nil { - select { - case <-connection.done: - default: - return connection, nil - } - } - t.access.Lock() - connection = t.connection - if connection != nil { - select { - case <-connection.done: - default: - t.access.Unlock() - return connection, nil - } - } - tcpConn, err := t.dialer.DialContext(t.ctx, "tcp", t.destination) - if err != nil { - return nil, err - } - connection = &dnsConnection{ - Conn: tcpConn, - done: make(chan struct{}), - callbacks: make(map[uint16]chan *dnsmessage.Message), - } - t.connection = connection - t.access.Unlock() - go t.newConnection(connection) - return connection, nil -} - -func (t *TCPTransport) newConnection(conn *dnsConnection) { - defer close(conn.done) - defer conn.Close() - err := task.Any(t.ctx, func(ctx context.Context) error { - return t.loopIn(conn) - }, func(ctx context.Context) error { - select { - case <-ctx.Done(): - return nil - case <-t.done: - return os.ErrClosed - } - }) - conn.err = err - if err != nil && !E.IsClosed(err) { - t.logger.Debug("connection closed: ", err) - } -} - -func (t *TCPTransport) loopIn(conn *dnsConnection) error { - _buffer := buf.StackNewSize(1024) - defer common.KeepAlive(_buffer) - buffer := common.Dup(_buffer) - defer buffer.Release() - for { - buffer.FullReset() - _, err := buffer.ReadFullFrom(conn, 2) - if err != nil { - return err - } - length := binary.BigEndian.Uint16(buffer.Bytes()) - if length > 512 { - return E.New("invalid length received: ", length) - } - buffer.FullReset() - _, err = buffer.ReadFullFrom(conn, int(length)) - if err != nil { - return err - } - var message dnsmessage.Message - err = message.Unpack(buffer.Bytes()) - if err != nil { - return err - } - conn.access.Lock() - callback, loaded := conn.callbacks[message.ID] - if loaded { - delete(conn.callbacks, message.ID) - } - conn.access.Unlock() - if !loaded { - continue - } - callback <- &message - } -} - -type dnsConnection struct { - net.Conn - done chan struct{} - err error - access sync.Mutex - queryId uint16 - callbacks map[uint16]chan *dnsmessage.Message -} - -func (t *TCPTransport) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) { - var connection *dnsConnection - err := task.Run(ctx, func() error { - var innerErr error - connection, innerErr = t.offer() - return innerErr - }) - if err != nil { - return nil, err - } - connection.access.Lock() - connection.queryId++ - message.ID = connection.queryId - callback := make(chan *dnsmessage.Message) - connection.callbacks[message.ID] = callback - connection.access.Unlock() - _buffer := buf.StackNewSize(1024) - defer common.KeepAlive(_buffer) - buffer := common.Dup(_buffer) - defer buffer.Release() - length := buffer.Extend(2) - rawMessage, err := message.AppendPack(buffer.Index(2)) - if err != nil { - return nil, err - } - buffer.Truncate(2 + len(rawMessage)) - binary.BigEndian.PutUint16(length, uint16(len(rawMessage))) - err = task.Run(ctx, func() error { - return common.Error(connection.Write(buffer.Bytes())) - }) - if err != nil { - return nil, err - } - select { - case response := <-callback: - return response, nil - case <-connection.done: - return nil, connection.err - case <-ctx.Done(): - return nil, ctx.Err() - } -} diff --git a/dns/transport_test.go b/dns/transport_test.go deleted file mode 100644 index 3c4eafc1..00000000 --- a/dns/transport_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package dns_test - -import ( - "context" - "testing" - "time" - - C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/dns" - "github.com/sagernet/sing-box/log" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" - - "github.com/stretchr/testify/require" - "golang.org/x/net/dns/dnsmessage" -) - -func TestTCPDNS(t *testing.T) { - t.Parallel() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - transport := dns.NewTCPTransport(ctx, N.SystemDialer, log.NewNopLogger(), M.ParseSocksaddr("1.0.0.1:53")) - response, err := transport.Exchange(ctx, makeQuery()) - cancel() - require.NoError(t, err) - require.NotEmpty(t, response.Answers, "no answers") - for _, answer := range response.Answers { - t.Log(answer) - } -} - -func TestTLSDNS(t *testing.T) { - t.Parallel() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - transport := dns.NewTLSTransport(ctx, N.SystemDialer, log.NewNopLogger(), M.ParseSocksaddr("1.0.0.1:853")) - response, err := transport.Exchange(ctx, makeQuery()) - cancel() - require.NoError(t, err) - require.NotEmpty(t, response.Answers, "no answers") - for _, answer := range response.Answers { - t.Log(answer) - } -} - -func TestHTTPSDNS(t *testing.T) { - t.Parallel() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - transport := dns.NewHTTPSTransport(N.SystemDialer, "https://1.0.0.1:443/dns-query") - response, err := transport.Exchange(ctx, makeQuery()) - cancel() - require.NoError(t, err) - require.NotEmpty(t, response.Answers, "no answers") - for _, answer := range response.Answers { - t.Log(answer) - } -} - -func TestUDPDNS(t *testing.T) { - t.Parallel() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - transport := dns.NewUDPTransport(ctx, N.SystemDialer, log.NewNopLogger(), M.ParseSocksaddr("1.0.0.1:53")) - response, err := transport.Exchange(ctx, makeQuery()) - cancel() - require.NoError(t, err) - require.NotEmpty(t, response.Answers, "no answers") - for _, answer := range response.Answers { - t.Log(answer) - } -} - -func TestLocalDNS(t *testing.T) { - t.Parallel() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - transport := dns.NewLocalTransport() - response, err := transport.Lookup(ctx, "google.com", C.DomainStrategyAsIS) - cancel() - require.NoError(t, err) - require.NotEmpty(t, response, "no answers") - for _, answer := range response { - t.Log(answer) - } -} - -func makeQuery() *dnsmessage.Message { - message := &dnsmessage.Message{} - message.Header.ID = 1 - message.Header.RecursionDesired = true - message.Questions = append(message.Questions, dnsmessage.Question{ - Name: dnsmessage.MustNewName("google.com."), - Type: dnsmessage.TypeA, - Class: dnsmessage.ClassINET, - }) - return message -} diff --git a/dns/transport_tls.go b/dns/transport_tls.go deleted file mode 100644 index f4a4f759..00000000 --- a/dns/transport_tls.go +++ /dev/null @@ -1,182 +0,0 @@ -package dns - -import ( - "context" - "crypto/tls" - "encoding/binary" - "os" - - "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing/common" - "github.com/sagernet/sing/common/buf" - E "github.com/sagernet/sing/common/exceptions" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" - "github.com/sagernet/sing/common/task" - - "golang.org/x/net/dns/dnsmessage" -) - -var _ adapter.DNSTransport = (*TLSTransport)(nil) - -type TLSTransport struct { - myTransportAdapter -} - -func NewTLSTransport(ctx context.Context, dialer N.Dialer, logger log.Logger, destination M.Socksaddr) *TLSTransport { - return &TLSTransport{ - myTransportAdapter{ - ctx: ctx, - dialer: dialer, - logger: logger, - destination: destination, - done: make(chan struct{}), - }, - } -} - -func (t *TLSTransport) offer(ctx context.Context) (*dnsConnection, error) { - t.access.RLock() - connection := t.connection - t.access.RUnlock() - if connection != nil { - select { - case <-connection.done: - default: - return connection, nil - } - } - t.access.Lock() - connection = t.connection - if connection != nil { - select { - case <-connection.done: - default: - t.access.Unlock() - return connection, nil - } - } - tcpConn, err := t.dialer.DialContext(t.ctx, "tcp", t.destination) - if err != nil { - return nil, err - } - tlsConn := tls.Client(tcpConn, &tls.Config{ - ServerName: t.destination.AddrString(), - }) - err = task.Run(t.ctx, func() error { - return tlsConn.HandshakeContext(ctx) - }) - if err != nil { - return nil, err - } - connection = &dnsConnection{ - Conn: tlsConn, - done: make(chan struct{}), - callbacks: make(map[uint16]chan *dnsmessage.Message), - } - t.connection = connection - t.access.Unlock() - go t.newConnection(connection) - return connection, nil -} - -func (t *TLSTransport) newConnection(conn *dnsConnection) { - defer close(conn.done) - defer conn.Close() - err := task.Any(t.ctx, func(ctx context.Context) error { - return t.loopIn(conn) - }, func(ctx context.Context) error { - select { - case <-ctx.Done(): - return nil - case <-t.done: - return os.ErrClosed - } - }) - conn.err = err - if err != nil && !E.IsClosed(err) { - t.logger.Debug("connection closed: ", err) - } -} - -func (t *TLSTransport) loopIn(conn *dnsConnection) error { - _buffer := buf.StackNewSize(1024) - defer common.KeepAlive(_buffer) - buffer := common.Dup(_buffer) - defer buffer.Release() - for { - buffer.FullReset() - _, err := buffer.ReadFullFrom(conn, 2) - if err != nil { - return err - } - length := binary.BigEndian.Uint16(buffer.Bytes()) - if length > 512 { - return E.New("invalid length received: ", length) - } - buffer.FullReset() - _, err = buffer.ReadFullFrom(conn, int(length)) - if err != nil { - return err - } - var message dnsmessage.Message - err = message.Unpack(buffer.Bytes()) - if err != nil { - return err - } - conn.access.Lock() - callback, loaded := conn.callbacks[message.ID] - if loaded { - delete(conn.callbacks, message.ID) - } - conn.access.Unlock() - if !loaded { - continue - } - callback <- &message - } -} - -func (t *TLSTransport) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) { - var connection *dnsConnection - err := task.Run(ctx, func() error { - var innerErr error - connection, innerErr = t.offer(ctx) - return innerErr - }) - if err != nil { - return nil, err - } - connection.access.Lock() - connection.queryId++ - message.ID = connection.queryId - callback := make(chan *dnsmessage.Message) - connection.callbacks[message.ID] = callback - connection.access.Unlock() - _buffer := buf.StackNewSize(1024) - defer common.KeepAlive(_buffer) - buffer := common.Dup(_buffer) - defer buffer.Release() - length := buffer.Extend(2) - rawMessage, err := message.AppendPack(buffer.Index(2)) - if err != nil { - return nil, err - } - buffer.Truncate(2 + len(rawMessage)) - binary.BigEndian.PutUint16(length, uint16(len(rawMessage))) - err = task.Run(ctx, func() error { - return common.Error(connection.Write(buffer.Bytes())) - }) - if err != nil { - return nil, err - } - select { - case response := <-callback: - return response, nil - case <-connection.done: - return nil, connection.err - case <-ctx.Done(): - return nil, ctx.Err() - } -} diff --git a/dns/transport_udp.go b/dns/transport_udp.go deleted file mode 100644 index df5aba5b..00000000 --- a/dns/transport_udp.go +++ /dev/null @@ -1,160 +0,0 @@ -package dns - -import ( - "context" - "os" - - "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing/common" - "github.com/sagernet/sing/common/buf" - E "github.com/sagernet/sing/common/exceptions" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" - "github.com/sagernet/sing/common/task" - - "golang.org/x/net/dns/dnsmessage" -) - -var _ adapter.DNSTransport = (*UDPTransport)(nil) - -type UDPTransport struct { - myTransportAdapter -} - -func NewUDPTransport(ctx context.Context, dialer N.Dialer, logger log.Logger, destination M.Socksaddr) *UDPTransport { - return &UDPTransport{ - myTransportAdapter{ - ctx: ctx, - dialer: dialer, - logger: logger, - destination: destination, - done: make(chan struct{}), - }, - } -} - -func (t *UDPTransport) offer() (*dnsConnection, error) { - t.access.RLock() - connection := t.connection - t.access.RUnlock() - if connection != nil { - select { - case <-connection.done: - default: - return connection, nil - } - } - t.access.Lock() - connection = t.connection - if connection != nil { - select { - case <-connection.done: - default: - t.access.Unlock() - return connection, nil - } - } - tcpConn, err := t.dialer.DialContext(t.ctx, "udp", t.destination) - if err != nil { - return nil, err - } - connection = &dnsConnection{ - Conn: tcpConn, - done: make(chan struct{}), - callbacks: make(map[uint16]chan *dnsmessage.Message), - } - t.connection = connection - t.access.Unlock() - go t.newConnection(connection) - return connection, nil -} - -func (t *UDPTransport) newConnection(conn *dnsConnection) { - defer close(conn.done) - defer conn.Close() - err := task.Any(t.ctx, func(ctx context.Context) error { - return t.loopIn(conn) - }, func(ctx context.Context) error { - select { - case <-ctx.Done(): - return nil - case <-t.done: - return os.ErrClosed - } - }) - conn.err = err - if err != nil && !E.IsClosed(err) { - t.logger.Debug("connection closed: ", err) - } -} - -func (t *UDPTransport) loopIn(conn *dnsConnection) error { - _buffer := buf.StackNewSize(1024) - defer common.KeepAlive(_buffer) - buffer := common.Dup(_buffer) - defer buffer.Release() - for { - buffer.FullReset() - _, err := buffer.ReadFrom(conn) - if err != nil { - return err - } - var message dnsmessage.Message - err = message.Unpack(buffer.Bytes()) - if err != nil { - return err - } - conn.access.Lock() - callback, loaded := conn.callbacks[message.ID] - if loaded { - delete(conn.callbacks, message.ID) - } - conn.access.Unlock() - if !loaded { - continue - } - callback <- &message - } -} - -func (t *UDPTransport) Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error) { - var connection *dnsConnection - err := task.Run(ctx, func() error { - var innerErr error - connection, innerErr = t.offer() - return innerErr - }) - if err != nil { - return nil, err - } - connection.access.Lock() - connection.queryId++ - message.ID = connection.queryId - callback := make(chan *dnsmessage.Message) - connection.callbacks[message.ID] = callback - connection.access.Unlock() - _buffer := buf.StackNewSize(1024) - defer common.KeepAlive(_buffer) - buffer := common.Dup(_buffer) - defer buffer.Release() - rawMessage, err := message.AppendPack(buffer.Index(0)) - if err != nil { - return nil, err - } - buffer.Truncate(len(rawMessage)) - err = task.Run(ctx, func() error { - return common.Error(connection.Write(buffer.Bytes())) - }) - if err != nil { - return nil, err - } - select { - case response := <-callback: - return response, nil - case <-connection.done: - return nil, connection.err - case <-ctx.Done(): - return nil, ctx.Err() - } -} diff --git a/format.go b/format.go index 6840452d..7b30b9ac 100644 --- a/format.go +++ b/format.go @@ -1,7 +1,7 @@ package box //go:generate go install -v mvdan.cc/gofumpt@latest -//go:generate go install -v github.com/daixiang0/gci@latest +//go:generate go install -v github.com/daixiang0/gci@v0.4.0 //go:generate gofumpt -l -w . //go:generate gofmt -s -w . //go:generate gci write -s "standard,prefix(github.com/sagernet/),default" . diff --git a/go.mod b/go.mod index cebd30ba..a1e090ea 100644 --- a/go.mod +++ b/go.mod @@ -7,15 +7,16 @@ require ( github.com/goccy/go-json v0.9.8 github.com/logrusorgru/aurora v2.0.3+incompatible github.com/oschwald/maxminddb-golang v1.9.0 - github.com/sagernet/sing v0.0.0-20220710135805-84be1c5eb67a + github.com/sagernet/sing v0.0.0-20220711103842-d3fb2260ef61 + github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 + github.com/sagernet/sing-tun v0.0.0-20220711091522-4f7247190c96 github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.5.0 github.com/stretchr/testify v1.8.0 - github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86 + github.com/vishvananda/netlink v1.1.0 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d golang.org/x/net v0.0.0-20220708220712-1185a9018129 - gvisor.dev/gvisor v0.0.0-20220708233959-72bdef768f07 ) require ( @@ -31,5 +32,6 @@ require ( golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + gvisor.dev/gvisor v0.0.0-20220711011657-cecae2f4234d // indirect lukechampine.com/blake3 v1.1.7 // indirect ) diff --git a/go.sum b/go.sum index 6c7ce94f..b80bb26d 100644 --- a/go.sum +++ b/go.sum @@ -25,10 +25,14 @@ github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagernet/sing v0.0.0-20220710135805-84be1c5eb67a h1:1SquyxA41EGvKGBrhj/HQkj4zhteThBPRFvJby0k2HE= -github.com/sagernet/sing v0.0.0-20220710135805-84be1c5eb67a/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c= +github.com/sagernet/sing v0.0.0-20220711103842-d3fb2260ef61 h1:sOx7t+MFssiCAY2afRHQSmkWZNpLQnjF0Hwv/TNVMvk= +github.com/sagernet/sing v0.0.0-20220711103842-d3fb2260ef61/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c= +github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 h1:oHbOmq1WS0XaZmXp6WpxzyB2xeyRIA1/L8EJKuNntfY= +github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619/go.mod h1:y2fpvoxukw3G7eApIZwkcpcG/NE4AB8pCQI0Qd8rMqk= github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k= github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw= +github.com/sagernet/sing-tun v0.0.0-20220711091522-4f7247190c96 h1:BPsCEEKmww4PCuL2qCKGpwuS/HllNz4/G7EjvSHlXXg= +github.com/sagernet/sing-tun v0.0.0-20220711091522-4f7247190c96/go.mod h1:OLQnVTGk8NMVdoegQvenGHsGEv3diSMWe9Uh02cel0E= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= @@ -41,14 +45,16 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86 h1:7SWt9pGCMaw+N1ZhRsaLKaYNviFhxambdoaoYlDqz1w= -github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0= golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I= @@ -62,7 +68,7 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gvisor.dev/gvisor v0.0.0-20220708233959-72bdef768f07 h1:EBa4BJfuxvdpvMGq6gw347MDptuFgqDHhuTEKxCCQ5U= -gvisor.dev/gvisor v0.0.0-20220708233959-72bdef768f07/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM= +gvisor.dev/gvisor v0.0.0-20220711011657-cecae2f4234d h1:KjI6i6P1ib9DiNdNIN8pb2TXfBewpKHf3O58cjj9vw4= +gvisor.dev/gvisor v0.0.0-20220711011657-cecae2f4234d/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= diff --git a/inbound/default.go b/inbound/default.go index 76027c3c..8b7f50c7 100644 --- a/inbound/default.go +++ b/inbound/default.go @@ -12,6 +12,7 @@ import ( C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" @@ -135,7 +136,7 @@ func (a *myInboundAdapter) loopTCPIn() { metadata.Inbound = a.tag metadata.SniffEnabled = a.listenOptions.SniffEnabled metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination - metadata.DomainStrategy = C.DomainStrategy(a.listenOptions.DomainStrategy) + metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy) metadata.Network = C.NetworkTCP metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()) a.logger.WithContext(ctx).Info("inbound connection from ", metadata.Source) @@ -168,7 +169,7 @@ func (a *myInboundAdapter) loopUDPIn() { metadata.Inbound = a.tag metadata.SniffEnabled = a.listenOptions.SniffEnabled metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination - metadata.DomainStrategy = C.DomainStrategy(a.listenOptions.DomainStrategy) + metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy) metadata.Network = C.NetworkUDP metadata.Source = M.SocksaddrFromNetIP(addr) err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata) @@ -193,7 +194,7 @@ func (a *myInboundAdapter) loopUDPInThreadSafe() { metadata.Inbound = a.tag metadata.SniffEnabled = a.listenOptions.SniffEnabled metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination - metadata.DomainStrategy = C.DomainStrategy(a.listenOptions.DomainStrategy) + metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy) metadata.Network = C.NetworkUDP metadata.Source = M.SocksaddrFromNetIP(addr) err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata) diff --git a/inbound/tun.go b/inbound/tun.go index 2665e43c..ea37cddc 100644 --- a/inbound/tun.go +++ b/inbound/tun.go @@ -12,10 +12,11 @@ import ( "strings" "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/common/tun" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing-dns" + "github.com/sagernet/sing-tun" E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" M "github.com/sagernet/sing/common/metadata" @@ -106,6 +107,7 @@ func (t *Tun) Close() error { } func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata M.Metadata) error { + ctx = log.ContextWithID(ctx) var metadata adapter.InboundContext metadata.Inbound = t.tag metadata.Network = C.NetworkTCP @@ -113,7 +115,7 @@ func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata metadata.Destination = upstreamMetadata.Destination metadata.SniffEnabled = t.inboundOptions.SniffEnabled metadata.SniffOverrideDestination = t.inboundOptions.SniffOverrideDestination - metadata.DomainStrategy = C.DomainStrategy(t.inboundOptions.DomainStrategy) + metadata.DomainStrategy = dns.DomainStrategy(t.inboundOptions.DomainStrategy) if t.hijackDNS && upstreamMetadata.Destination.Port == 53 { return task.Run(ctx, func() error { return NewDNSConnection(ctx, t.router, t.logger, conn, metadata) @@ -125,6 +127,7 @@ func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata } func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstreamMetadata M.Metadata) error { + ctx = log.ContextWithID(ctx) var metadata adapter.InboundContext metadata.Inbound = t.tag metadata.Network = C.NetworkUDP @@ -132,7 +135,7 @@ func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstre metadata.Destination = upstreamMetadata.Destination metadata.SniffEnabled = t.inboundOptions.SniffEnabled metadata.SniffOverrideDestination = t.inboundOptions.SniffOverrideDestination - metadata.DomainStrategy = C.DomainStrategy(t.inboundOptions.DomainStrategy) + metadata.DomainStrategy = dns.DomainStrategy(t.inboundOptions.DomainStrategy) if t.hijackDNS && upstreamMetadata.Destination.Port == 53 { return task.Run(ctx, func() error { return NewDNSPacketConnection(ctx, t.router, t.logger, conn, metadata) diff --git a/option/dns.go b/option/dns.go index 8909437f..c39aef05 100644 --- a/option/dns.go +++ b/option/dns.go @@ -29,11 +29,12 @@ type DNSClientOptions struct { } type DNSServerOptions struct { - Tag string `json:"tag,omitempty"` - Address string `json:"address"` - AddressResolver string `json:"address_resolver,omitempty"` - AddressStrategy DomainStrategy `json:"address_strategy,omitempty"` - Detour string `json:"detour,omitempty"` + Tag string `json:"tag,omitempty"` + Address string `json:"address"` + AddressResolver string `json:"address_resolver,omitempty"` + AddressStrategy DomainStrategy `json:"address_strategy,omitempty"` + AddressFallbackDelay Duration `json:"address_fallback_delay,omitempty"` + Detour string `json:"detour,omitempty"` } type _DNSRule struct { diff --git a/option/types.go b/option/types.go index c925cbe5..978ee2ab 100644 --- a/option/types.go +++ b/option/types.go @@ -6,6 +6,7 @@ import ( "time" C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-dns" E "github.com/sagernet/sing/common/exceptions" "github.com/goccy/go-json" @@ -91,21 +92,21 @@ func (l *Listable[T]) UnmarshalJSON(content []byte) error { return nil } -type DomainStrategy C.DomainStrategy +type DomainStrategy dns.DomainStrategy func (s DomainStrategy) MarshalJSON() ([]byte, error) { var value string - switch C.DomainStrategy(s) { - case C.DomainStrategyAsIS: + switch dns.DomainStrategy(s) { + case dns.DomainStrategyAsIS: value = "" // value = "AsIS" - case C.DomainStrategyPreferIPv4: + case dns.DomainStrategyPreferIPv4: value = "prefer_ipv4" - case C.DomainStrategyPreferIPv6: + case dns.DomainStrategyPreferIPv6: value = "prefer_ipv6" - case C.DomainStrategyUseIPv4: + case dns.DomainStrategyUseIPv4: value = "ipv4_only" - case C.DomainStrategyUseIPv6: + case dns.DomainStrategyUseIPv6: value = "ipv6_only" default: return nil, E.New("unknown domain strategy: ", s) @@ -121,15 +122,15 @@ func (s *DomainStrategy) UnmarshalJSON(bytes []byte) error { } switch value { case "", "AsIS": - *s = DomainStrategy(C.DomainStrategyAsIS) + *s = DomainStrategy(dns.DomainStrategyAsIS) case "prefer_ipv4": - *s = DomainStrategy(C.DomainStrategyPreferIPv4) + *s = DomainStrategy(dns.DomainStrategyPreferIPv4) case "prefer_ipv6": - *s = DomainStrategy(C.DomainStrategyPreferIPv6) + *s = DomainStrategy(dns.DomainStrategyPreferIPv6) case "ipv4_only": - *s = DomainStrategy(C.DomainStrategyUseIPv4) + *s = DomainStrategy(dns.DomainStrategyUseIPv4) case "ipv6_only": - *s = DomainStrategy(C.DomainStrategyUseIPv6) + *s = DomainStrategy(dns.DomainStrategyUseIPv6) default: return E.New("unknown domain strategy: ", value) } diff --git a/outbound/default.go b/outbound/default.go index 1fafe7fa..2be608d0 100644 --- a/outbound/default.go +++ b/outbound/default.go @@ -7,7 +7,6 @@ import ( "time" "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/common/dialer" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing/common" @@ -41,7 +40,7 @@ func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata a var outConn net.Conn var err error if len(metadata.DestinationAddresses) > 0 { - outConn, err = dialer.DialSerial(ctx, this, C.NetworkTCP, metadata.Destination, metadata.DestinationAddresses) + outConn, err = N.DialSerial(ctx, this, C.NetworkTCP, metadata.Destination, metadata.DestinationAddresses) } else { outConn, err = this.DialContext(ctx, C.NetworkTCP, metadata.Destination) } @@ -56,7 +55,7 @@ func NewEarlyConnection(ctx context.Context, this N.Dialer, conn net.Conn, metad var outConn net.Conn var err error if len(metadata.DestinationAddresses) > 0 { - outConn, err = dialer.DialSerial(ctx, this, C.NetworkTCP, metadata.Destination, metadata.DestinationAddresses) + outConn, err = N.DialSerial(ctx, this, C.NetworkTCP, metadata.Destination, metadata.DestinationAddresses) } else { outConn, err = this.DialContext(ctx, C.NetworkTCP, metadata.Destination) } @@ -71,7 +70,7 @@ func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, var outConn net.PacketConn var err error if len(metadata.DestinationAddresses) > 0 { - outConn, err = dialer.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses) + outConn, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses) } else { outConn, err = this.ListenPacket(ctx, metadata.Destination) } diff --git a/route/router.go b/route/router.go index 22cab96d..d34a4ea3 100644 --- a/route/router.go +++ b/route/router.go @@ -20,9 +20,9 @@ import ( "github.com/sagernet/sing-box/common/iffmonitor" "github.com/sagernet/sing-box/common/sniff" C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/dns" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/bufio" @@ -57,13 +57,12 @@ type Router struct { geositeReader *geosite.Reader geositeCache map[string]adapter.Rule - dnsClient adapter.DNSClient - defaultDomainStrategy C.DomainStrategy + dnsClient *dns.Client + defaultDomainStrategy dns.DomainStrategy dnsRules []adapter.Rule - - defaultTransport adapter.DNSTransport - transports []adapter.DNSTransport - transportMap map[string]adapter.DNSTransport + defaultTransport dns.Transport + transports []dns.Transport + transportMap map[string]dns.Transport autoDetectInterface bool interfaceMonitor iffmonitor.InterfaceMonitor @@ -83,8 +82,8 @@ func NewRouter(ctx context.Context, logger log.Logger, options option.RouteOptio geositeOptions: common.PtrValueOrDefault(options.Geosite), geositeCache: make(map[string]adapter.Rule), defaultDetour: options.Final, - dnsClient: dns.NewClient(dnsOptions.DNSClientOptions), - defaultDomainStrategy: C.DomainStrategy(dnsOptions.Strategy), + dnsClient: dns.NewClient(dns.DomainStrategy(dnsOptions.DNSClientOptions.Strategy), dnsOptions.DNSClientOptions.DisableCache, dnsOptions.DNSClientOptions.DisableExpire), + defaultDomainStrategy: dns.DomainStrategy(dnsOptions.Strategy), autoDetectInterface: options.AutoDetectInterface, } for i, ruleOptions := range options.Rules { @@ -101,9 +100,9 @@ func NewRouter(ctx context.Context, logger log.Logger, options option.RouteOptio } router.dnsRules = append(router.dnsRules, dnsRule) } - transports := make([]adapter.DNSTransport, len(dnsOptions.Servers)) - dummyTransportMap := make(map[string]adapter.DNSTransport) - transportMap := make(map[string]adapter.DNSTransport) + transports := make([]dns.Transport, len(dnsOptions.Servers)) + dummyTransportMap := make(map[string]dns.Transport) + transportMap := make(map[string]dns.Transport) transportTags := make([]string, len(dnsOptions.Servers)) transportTagMap := make(map[string]bool) for i, server := range dnsOptions.Servers { @@ -144,7 +143,7 @@ func NewRouter(ctx context.Context, logger log.Logger, options option.RouteOptio return nil, E.New("parse dns server[", tag, "]: address resolver not found: ", server.AddressResolver) } if upstream, exists := dummyTransportMap[server.AddressResolver]; exists { - detour = dns.NewDialerWrapper(detour, C.DomainStrategy(server.AddressStrategy), router.dnsClient, upstream) + detour = dns.NewDialerWrapper(detour, router.dnsClient, upstream, dns.DomainStrategy(server.AddressStrategy), time.Duration(server.AddressFallbackDelay)) } else { continue } @@ -152,7 +151,7 @@ func NewRouter(ctx context.Context, logger log.Logger, options option.RouteOptio return nil, E.New("parse dns server[", tag, "]: missing address_resolver") } } - transport, err := dns.NewTransport(ctx, detour, logger, server.Address) + transport, err := dns.NewTransport(ctx, detour, server.Address) if err != nil { return nil, E.Cause(err, "parse dns server[", tag, "]") } @@ -176,7 +175,7 @@ func NewRouter(ctx context.Context, logger log.Logger, options option.RouteOptio }) return nil, E.New("found circular reference in dns servers: ", strings.Join(unresolvedTags, " ")) } - var defaultTransport adapter.DNSTransport + var defaultTransport dns.Transport if options.Final != "" { defaultTransport = dummyTransportMap[options.Final] if defaultTransport == nil { @@ -408,7 +407,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad conn = bufio.NewCachedConn(conn, buffer) } } - if metadata.Destination.IsFqdn() && metadata.DomainStrategy != C.DomainStrategyAsIS { + if metadata.Destination.IsFqdn() && metadata.DomainStrategy != dns.DomainStrategyAsIS { addresses, err := r.Lookup(adapter.WithContext(ctx, &metadata), metadata.Destination.Fqdn, metadata.DomainStrategy) if err != nil { return err @@ -450,7 +449,7 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m } conn = bufio.NewCachedPacketConn(conn, buffer, originDestination) } - if metadata.Destination.IsFqdn() && metadata.DomainStrategy != C.DomainStrategyAsIS { + if metadata.Destination.IsFqdn() && metadata.DomainStrategy != dns.DomainStrategyAsIS { addresses, err := r.Lookup(adapter.WithContext(ctx, &metadata), metadata.Destination.Fqdn, metadata.DomainStrategy) if err != nil { return err @@ -470,7 +469,7 @@ func (r *Router) Exchange(ctx context.Context, message *dnsmessage.Message) (*dn return r.dnsClient.Exchange(ctx, r.matchDNS(ctx), message) } -func (r *Router) Lookup(ctx context.Context, domain string, strategy C.DomainStrategy) ([]netip.Addr, error) { +func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) { return r.dnsClient.Lookup(ctx, r.matchDNS(ctx), domain, strategy) } @@ -492,7 +491,7 @@ func (r *Router) match(ctx context.Context, metadata adapter.InboundContext, def return defaultOutbound } -func (r *Router) matchDNS(ctx context.Context) adapter.DNSTransport { +func (r *Router) matchDNS(ctx context.Context) dns.Transport { metadata := adapter.ContextFrom(ctx) if metadata == nil { r.dnsLogger.WithContext(ctx).Warn("no context: ", reflect.TypeOf(ctx)) diff --git a/route/rule_domain.go b/route/rule_domain.go index dfdee095..619f11bf 100644 --- a/route/rule_domain.go +++ b/route/rule_domain.go @@ -4,8 +4,8 @@ import ( "strings" "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/common/domain" "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/domain" ) var _ RuleItem = (*DomainItem)(nil) diff --git a/test/go.mod b/test/go.mod index 25dd809b..8e0980e1 100644 --- a/test/go.mod +++ b/test/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/docker/docker v20.10.17+incompatible github.com/docker/go-connections v0.4.0 - github.com/sagernet/sing v0.0.0-20220710135805-84be1c5eb67a + github.com/sagernet/sing v0.0.0-20220711103842-d3fb2260ef61 github.com/sagernet/sing-box v0.0.0 github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.8.0 @@ -33,14 +33,16 @@ require ( github.com/oschwald/maxminddb-golang v1.9.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 // indirect github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 // indirect - github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86 // indirect + github.com/sagernet/sing-tun v0.0.0-20220711091522-4f7247190c96 // indirect + github.com/vishvananda/netlink v1.1.0 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.3.0 // indirect - gvisor.dev/gvisor v0.0.0-20220708233959-72bdef768f07 // indirect + gvisor.dev/gvisor v0.0.0-20220711011657-cecae2f4234d // indirect lukechampine.com/blake3 v1.1.7 // indirect ) diff --git a/test/go.sum b/test/go.sum index 5d2e8e88..40d678d8 100644 --- a/test/go.sum +++ b/test/go.sum @@ -52,10 +52,14 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sagernet/sing v0.0.0-20220710135805-84be1c5eb67a h1:1SquyxA41EGvKGBrhj/HQkj4zhteThBPRFvJby0k2HE= -github.com/sagernet/sing v0.0.0-20220710135805-84be1c5eb67a/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c= +github.com/sagernet/sing v0.0.0-20220711103842-d3fb2260ef61 h1:sOx7t+MFssiCAY2afRHQSmkWZNpLQnjF0Hwv/TNVMvk= +github.com/sagernet/sing v0.0.0-20220711103842-d3fb2260ef61/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c= +github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 h1:oHbOmq1WS0XaZmXp6WpxzyB2xeyRIA1/L8EJKuNntfY= +github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619/go.mod h1:y2fpvoxukw3G7eApIZwkcpcG/NE4AB8pCQI0Qd8rMqk= github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k= github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw= +github.com/sagernet/sing-tun v0.0.0-20220711091522-4f7247190c96 h1:BPsCEEKmww4PCuL2qCKGpwuS/HllNz4/G7EjvSHlXXg= +github.com/sagernet/sing-tun v0.0.0-20220711091522-4f7247190c96/go.mod h1:OLQnVTGk8NMVdoegQvenGHsGEv3diSMWe9Uh02cel0E= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -66,8 +70,9 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86 h1:7SWt9pGCMaw+N1ZhRsaLKaYNviFhxambdoaoYlDqz1w= -github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -91,6 +96,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -121,7 +127,7 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= -gvisor.dev/gvisor v0.0.0-20220708233959-72bdef768f07 h1:EBa4BJfuxvdpvMGq6gw347MDptuFgqDHhuTEKxCCQ5U= -gvisor.dev/gvisor v0.0.0-20220708233959-72bdef768f07/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM= +gvisor.dev/gvisor v0.0.0-20220711011657-cecae2f4234d h1:KjI6i6P1ib9DiNdNIN8pb2TXfBewpKHf3O58cjj9vw4= +gvisor.dev/gvisor v0.0.0-20220711011657-cecae2f4234d/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=