diff --git a/common/networkquality/networkquality.go b/common/networkquality/networkquality.go index 3247705d9..447c89c77 100644 --- a/common/networkquality/networkquality.go +++ b/common/networkquality/networkquality.go @@ -407,11 +407,9 @@ func (r *directionRunner) addConnection(ctx context.Context) error { r.connMu.Lock() r.connections = append(r.connections, conn) r.connMu.Unlock() - r.wg.Add(1) - go func() { - defer r.wg.Done() + r.wg.Go(func() { conn.run(ctx, r.onConnectionFailed) - }() + }) return nil } @@ -437,9 +435,7 @@ func (r *directionRunner) pickReadyConnection() *loadConnection { } func (r *directionRunner) startProber(ctx context.Context) { - r.wg.Add(1) - go func() { - defer r.wg.Done() + r.wg.Go(func() { ticker := time.NewTicker(r.probeInterval()) defer ticker.Stop() for { @@ -455,7 +451,7 @@ func (r *directionRunner) startProber(ctx context.Context) { r.runProbeRound(ctx, conn.client) ticker.Reset(r.probeInterval()) } - }() + }) } func (r *directionRunner) runProbeRound(ctx context.Context, selfClient *http.Client) { diff --git a/daemon/started_service_tailscale_ssh.go b/daemon/started_service_tailscale_ssh.go index c3030fe5c..bc8b37269 100644 --- a/daemon/started_service_tailscale_ssh.go +++ b/daemon/started_service_tailscale_ssh.go @@ -211,9 +211,7 @@ func (s *StartedService) StartTailscaleSSHSession( Message: &TailscaleSSHServerMessage_Ready{Ready: &TailscaleSSHReady{}}, }) - workersWg.Add(1) - go func() { - defer workersWg.Done() + workersWg.Go(func() { for { msg, recvErr := server.Recv() if recvErr == io.EOF { @@ -243,10 +241,9 @@ func (s *StartedService) StartTailscaleSSHSession( })) } } - }() + }) pumpReader := func(reader io.Reader) { - defer workersWg.Done() buffer := buf.Get(buf.BufferSize) defer buf.Put(buffer) for { @@ -261,14 +258,10 @@ func (s *StartedService) StartTailscaleSSHSession( } } } - workersWg.Add(1) - go pumpReader(stdout) - workersWg.Add(1) - go pumpReader(stderr) + workersWg.Go(func() { pumpReader(stdout) }) + workersWg.Go(func() { pumpReader(stderr) }) - workersWg.Add(1) - go func() { - defer workersWg.Done() + workersWg.Go(func() { ticker := time.NewTicker(30 * time.Second) defer ticker.Stop() for { @@ -283,11 +276,9 @@ func (s *StartedService) StartTailscaleSSHSession( } } } - }() + }) - workersWg.Add(1) - go func() { - defer workersWg.Done() + workersWg.Go(func() { waitErr := sshSession.Wait() exitMessage := &TailscaleSSHExit{} switch waitErrTyped := waitErr.(type) { @@ -302,7 +293,7 @@ func (s *StartedService) StartTailscaleSSHSession( Message: &TailscaleSSHServerMessage_Exit{Exit: exitMessage}, }) cancel() - }() + }) go func() { <-sessionCtx.Done() diff --git a/dns/transport/local/local_darwin_test.go b/dns/transport/local/local_darwin_test.go index 7db18c8dc..52bb3ed66 100644 --- a/dns/transport/local/local_darwin_test.go +++ b/dns/transport/local/local_darwin_test.go @@ -134,9 +134,7 @@ func TestSystemExchangeConcurrent(t *testing.T) { if i%2 == 1 { qtype = mDNS.TypeAAAA } - waitGroup.Add(1) - go func() { - defer waitGroup.Done() + waitGroup.Go(func() { message := new(mDNS.Msg) message.SetQuestion("localhost.", qtype) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) @@ -149,7 +147,7 @@ func TestSystemExchangeConcurrent(t *testing.T) { if len(response.Answer) == 0 { errors <- context.DeadlineExceeded } - }() + }) } waitGroup.Wait() close(errors) diff --git a/experimental/libbox/command_client.go b/experimental/libbox/command_client.go index f15e7219a..d19f3e990 100644 --- a/experimental/libbox/command_client.go +++ b/experimental/libbox/command_client.go @@ -1178,9 +1178,7 @@ func (c *CommandClient) StartTailscaleSSHSession(opts *TailscaleSSHOptions, hand closeDone: make(chan struct{}), } - session.wg.Add(1) - go func() { - defer session.wg.Done() + session.wg.Go(func() { for { select { case <-streamCtx.Done(): @@ -1208,11 +1206,9 @@ func (c *CommandClient) StartTailscaleSSHSession(opts *TailscaleSSHOptions, hand } } } - }() + }) - session.wg.Add(1) - go func() { - defer session.wg.Done() + session.wg.Go(func() { for { msg, recvErr := stream.Recv() if recvErr == io.EOF { @@ -1239,7 +1235,7 @@ func (c *CommandClient) StartTailscaleSSHSession(opts *TailscaleSSHOptions, hand handler.OnError(payload.Error.Message) } } - }() + }) standalone := c.standalone go func() { diff --git a/go.mod b/go.mod index 38fd62483..2b225e7a4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/sagernet/sing-box -go 1.25.0 +go 1.25.5 require ( filippo.io/age v1.3.1 @@ -18,7 +18,7 @@ require ( github.com/godbus/dbus/v5 v5.2.2 github.com/gofrs/uuid/v5 v5.4.0 github.com/insomniacslk/dhcp v0.0.0-20260220084031-5adc3eb26f91 - github.com/jsimonetti/rtnetlink v1.4.0 + github.com/jsimonetti/rtnetlink v1.4.1 github.com/keybase/go-keychain v0.0.1 github.com/libdns/acmedns v0.5.0 github.com/libdns/alidns v1.0.6 @@ -58,8 +58,8 @@ require ( github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 github.com/sagernet/smux v1.5.50-sing-box-mod.1 - github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.10 - github.com/sagernet/wireguard-go v0.0.5-0.20260717024847-6f5e8b1947ae + github.com/sagernet/tailscale v1.102.1-sing-box-1.14-mod.2 + github.com/sagernet/wireguard-go v0.0.5-0.20260805042941-f39689ad3562 github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.11.1 @@ -68,7 +68,7 @@ require ( go.uber.org/zap v1.27.1 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba golang.org/x/crypto v0.54.0 - golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 + golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f golang.org/x/mod v0.37.0 golang.org/x/net v0.57.0 golang.org/x/sync v0.22.0 @@ -81,7 +81,7 @@ require ( ) require ( - filippo.io/edwards25519 v1.1.0 // indirect + filippo.io/edwards25519 v1.2.0 // indirect filippo.io/hpke v0.4.0 // indirect github.com/RyuaNerin/go-krypto v1.3.0 // indirect github.com/ajg/form v1.5.1 // indirect @@ -90,6 +90,7 @@ require ( github.com/anchore/go-lzo v0.1.0 // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect + github.com/axiomhq/hyperloglog v0.0.0-20240319100328-84253e514e02 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect github.com/coreos/go-oidc/v3 v3.17.0 // indirect @@ -97,13 +98,15 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d // indirect + github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/florianl/go-nfqueue/v2 v2.1.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/gaissmai/bart v0.18.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/gaissmai/bart v0.26.1 // indirect github.com/go-jose/go-jose/v4 v4.1.3 // indirect github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go4org/hashtriemap v0.0.0-20251130024219-545ba229f689 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect @@ -116,10 +119,10 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/yamux v0.1.2 // indirect github.com/hdevalence/ed25519consensus v0.2.0 // indirect - github.com/huin/goupnp v1.2.0 // indirect + github.com/huin/goupnp v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect - github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/compress v1.19.1 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/koron/go-ssdp v0.0.4 // indirect github.com/kr/fs v0.1.0 // indirect @@ -128,13 +131,12 @@ require ( github.com/mdlayher/socket v0.6.0 // indirect github.com/mitchellh/go-ps v1.0.0 // indirect github.com/philhofer/fwd v1.2.0 // indirect - github.com/pierrec/lz4/v4 v4.1.21 // indirect + github.com/pierrec/lz4/v4 v4.1.26 // indirect github.com/pion/dtls/v3 v3.1.5 // indirect github.com/pion/logging v0.2.4 // indirect github.com/pion/transport/v4 v4.0.2 // indirect github.com/pires/go-proxyproto v0.8.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus-community/pro-bing v0.4.0 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/safchain/ethtool v0.3.0 // indirect github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260807161529-8d42107dcdfc // indirect @@ -168,9 +170,8 @@ require ( github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260807161529-8d42107dcdfc // indirect github.com/smallstep/pkcs7 v0.1.1 // indirect github.com/spf13/pflag v1.0.9 // indirect - github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect - github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect - github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect + github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d // indirect + github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd // indirect github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect @@ -186,7 +187,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect - golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/term v0.45.0 // indirect golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.47.0 // indirect diff --git a/go.sum b/go.sum index fb4823174..7036262af 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ code.pfad.fr/check v1.1.0 h1:GWvjdzhSEgHvEHe2uJujDcpmZoySKuHQNrZMfzfO0bE= code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM= filippo.io/age v1.3.1 h1:hbzdQOJkuaMEpRCLSN1/C5DX74RPcNCk6oqhKMXmZi0= filippo.io/age v1.3.1/go.mod h1:EZorDTYUxt836i3zdori5IJX/v2Lj6kWFU0cfh6C0D4= -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= filippo.io/hpke v0.4.0 h1:p575VVQ6ted4pL+it6M00V/f2qTZITO0zgmdKCkd5+A= filippo.io/hpke v0.4.0/go.mod h1:EmAN849/P3qdeK+PCMkDpDm83vRHM5cDipBJ8xbQLVY= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -30,6 +30,8 @@ github.com/anthropics/anthropic-sdk-go v1.26.0 h1:oUTzFaUpAevfuELAP1sjL6CQJ9HHAf github.com/anthropics/anthropic-sdk-go v1.26.0/go.mod h1:qUKmaW+uuPB64iy1l+4kOSvaLqPXnHTTBKH6RVZ7q5Q= github.com/anytls/sing-anytls v0.0.11 h1:w8e9Uj1oP3m4zxkyZDewPk0EcQbvVxb7Nn+rapEx4fc= github.com/anytls/sing-anytls v0.0.11/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= +github.com/axiomhq/hyperloglog v0.0.0-20240319100328-84253e514e02 h1:bXAPYSbdYbS5VTy92NIUbeDI1qyggi+JYh5op9IFlcQ= +github.com/axiomhq/hyperloglog v0.0.0-20240319100328-84253e514e02/go.mod h1:k08r+Yj1PRAmuayFiRK6MYuR5Ve4IuZtTfxErMIh0+c= github.com/caddyserver/certmagic v0.25.3-0.20260421143802-60d9d8b415d6 h1:LYSB6VgWzKtNrcxElw3c97BP40Oc7bizKxA9K1Vi/5k= github.com/caddyserver/certmagic v0.25.3-0.20260421143802-60d9d8b415d6/go.mod h1:llW/CvsNmza8S6hmsuggsZeiX+uS27dkqY27wDIuBWg= github.com/caddyserver/zerossl v0.1.5 h1:dkvOjBAEEtY6LIGAHei7sw2UgqSD6TrWweXpV7lvEvE= @@ -39,8 +41,8 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk= -github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= +github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= +github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g= @@ -59,6 +61,7 @@ github.com/database64128/netx-go v0.1.1/go.mod h1:LNlYVipaYkQArRFDNNJ02VkNV+My9A github.com/database64128/tfo-go/v2 v2.3.2 h1:UhZMKiMq3swZGUiETkLBDzQnZBPSAeBMClpJGlnJ5Fw= github.com/database64128/tfo-go/v2 v2.3.2/go.mod h1:GC3uB5oa4beGpCUbRb2ZOWP73bJJFmMyAVgQSO7r724= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk= @@ -67,6 +70,8 @@ github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbY github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d h1:CPqTNIigGweVPT4CYb+OO2E6XyRKFOmvTHwWRLgCAlE= github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d/go.mod h1:QX5ZVULjAfZJux/W62Y91HvCh9hyW6enAwcrrv/sLj0= +github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc h1:8WFBn63wegobsYAX0YjD+8suexZDga5CctH4CCTx2+8= +github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= @@ -78,10 +83,10 @@ github.com/florianl/go-nfqueue/v2 v2.1.0 h1:Fywt30TY/evxyDySpXjxQ1jsRW7nQbLpOhEL github.com/florianl/go-nfqueue/v2 v2.1.0/go.mod h1:8PKUM5rYoVFO5IZV1bifx4/b0jHAglKkHXr9PRwzi4Y= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/gaissmai/bart v0.18.0 h1:jQLBT/RduJu0pv/tLwXE+xKPgtWJejbxuXAR+wLJafo= -github.com/gaissmai/bart v0.18.0/go.mod h1:JJzMAhNF5Rjo4SF4jWBrANuJfqY+FvsFhW7t1UZJ+XY= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/gaissmai/bart v0.26.1 h1:+w4rnLGNlA2GDVn382Tfe3jOsK5vOr5n4KmigJ9lbTo= +github.com/gaissmai/bart v0.26.1/go.mod h1:GREWQfTLRWz/c5FTOsIw+KkscuFkIV5t8Rp7Nd1Td5c= github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I= github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo= github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug= @@ -96,6 +101,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go4org/hashtriemap v0.0.0-20251130024219-545ba229f689 h1:0psnKZ+N2IP43/SZC8SKx6OpFJwLmQb9m9QyV9BC2f8= +github.com/go4org/hashtriemap v0.0.0-20251130024219-545ba229f689/go.mod h1:OGmRfY/9QEK2P5zCRtmqfbCF283xPkU2dvVA4MvbvpI= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= @@ -143,8 +150,8 @@ github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8 github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU= github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= -github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY= -github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/insomniacslk/dhcp v0.0.0-20260220084031-5adc3eb26f91 h1:u9i04mGE3iliBh0EFuWaKsmcwrLacqGmq1G3XoaM7gY= @@ -152,14 +159,14 @@ github.com/insomniacslk/dhcp v0.0.0-20260220084031-5adc3eb26f91/go.mod h1:qfvBmy github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I= -github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E= +github.com/jsimonetti/rtnetlink v1.4.1 h1:JfD4jthWBqZMEffc5RjgmlzpYttAVw1sdnmiNaPO3hE= +github.com/jsimonetti/rtnetlink v1.4.1/go.mod h1:xJjT7t59UIZ62GLZbv6PLLo8VFrostJMPBAheR6OM8w= github.com/jsimonetti/rtnetlink/v2 v2.2.0 h1:/KfZ310gOAFrXXol5VwnFEt+ucldD/0dsSRZwpHCP9w= github.com/jsimonetti/rtnetlink/v2 v2.2.0/go.mod h1:lbjDHxC+5RJ08lzPeA90Ls2pEoId3F08MoEMlhfHxeI= github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk= +github.com/klauspost/compress v1.19.1/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= @@ -206,8 +213,8 @@ github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5 github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8= github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= -github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= -github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.26 h1:GrpZw1gZttORinvzBdXPUXATeqlJjqUG/D87TKMnhjY= +github.com/pierrec/lz4/v4 v4.1.26/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pion/dtls/v3 v3.1.5 h1:9xJtVsHwMYeSjPp5Hh1FTis4DchnQWtnOa5o+6ygqfc= github.com/pion/dtls/v3 v3.1.5/go.mod h1:gz1K4jg6c+fq86oQMH4pilpCEOEPwmEr2jY+VcF/mkU= github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8= @@ -221,8 +228,6 @@ github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1Hbe github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyfyrrjEaAchdy3R4= -github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= @@ -339,10 +344,10 @@ github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 h1:aSwUNYUkV github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY= github.com/sagernet/smux v1.5.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478= github.com/sagernet/smux v1.5.50-sing-box-mod.1/go.mod h1:NjhsCEWedJm7eFLyhuBgIEzwfhRmytrUoiLluxs5Sk8= -github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.10 h1:KpTpzkDQKAlC2PCWB6AcsUQQ3ynnxVz0vY/XEP6HqEo= -github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.10/go.mod h1:mcjmzmnvYguJ8lPFVu46ZueMz8lizGWMUqZMUhhUMoI= -github.com/sagernet/wireguard-go v0.0.5-0.20260717024847-6f5e8b1947ae h1:GmxlXWnRmeNfPE1tWXRZIFgKJd5BH5okoDHKZkkI5bw= -github.com/sagernet/wireguard-go v0.0.5-0.20260717024847-6f5e8b1947ae/go.mod h1:hEqi4y5czEg6LYtX2Bpjg+lV0b/J1n+5rA885Z66Mx0= +github.com/sagernet/tailscale v1.102.1-sing-box-1.14-mod.2 h1:JXYUrqxvIYGVVvTz0xzYR6DufhLBfnV5wh5lwhbdJVM= +github.com/sagernet/tailscale v1.102.1-sing-box-1.14-mod.2/go.mod h1:WLUSOPmTcf7VN9gLCe01qUSIvD+/cKC177neENyZPkI= +github.com/sagernet/wireguard-go v0.0.5-0.20260805042941-f39689ad3562 h1:dhvhMpGaa+enzCS6FKozBwvPseiIOrGrmBCsAgCwoSY= +github.com/sagernet/wireguard-go v0.0.5-0.20260805042941-f39689ad3562/go.mod h1:er10sELpmzLXq7S7Pbc1Zsbyapcr+/gxNAHKTo6fzVA= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA= github.com/smallstep/pkcs7 v0.1.1 h1:x+rPdt2W088V9Vkjho4KtoggyktZJlMduZAtRHm68LU= @@ -355,14 +360,12 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ= -github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4= +github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d h1:JcGKBZAL7ePLwOhUdN8qGQZlP5GueEiIZwY7R62pejE= +github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4= github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4= github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg= -github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio= -github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8= -github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw= -github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8= +github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd h1:Rf9uhF1+VJ7ZHqxrG8pJ6YacmHvVCmByDmGbAWCc/gA= +github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo= github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU= github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0= github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+yfntqhI3oAu9i27nEojcQ4NuBQOo5ZFA= @@ -439,10 +442,10 @@ golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ss golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0= -golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU= -golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= -golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= +golang.org/x/image v0.41.0 h1:8wS72eGJMJaBxK6okTzd4WaXumUlTVlb753MlsSvTCo= +golang.org/x/image v0.41.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -473,8 +476,8 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= -golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/protocol/hysteria2/realm.go b/protocol/hysteria2/realm.go index 5b7a82e84..de5bdd9f2 100644 --- a/protocol/hysteria2/realm.go +++ b/protocol/hysteria2/realm.go @@ -24,7 +24,7 @@ import ( "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/render" "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" + "golang.org/x/net/http2/h2c" //nolint:staticcheck ) func RegisterRealmService(registry *boxService.Registry) { @@ -98,6 +98,7 @@ func NewRealmService(ctx context.Context, logger log.ContextLogger, tag string, Listen: options.ListenOptions, }), httpServer: &http.Server{ + //nolint:staticcheck Handler: h2c.NewHandler(chiRouter, &http2.Server{ IdleTimeout: time.Duration(options.IdleTimeout), ReadIdleTimeout: time.Duration(options.KeepAlivePeriod), diff --git a/protocol/naive/inbound.go b/protocol/naive/inbound.go index a70ff5bc0..603d67420 100644 --- a/protocol/naive/inbound.go +++ b/protocol/naive/inbound.go @@ -26,7 +26,7 @@ import ( sHttp "github.com/sagernet/sing/protocol/http" "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" + "golang.org/x/net/http2/h2c" //nolint:staticcheck ) var ( @@ -102,6 +102,7 @@ func (n *Inbound) Start(stage adapter.StartStage) error { return err } n.httpServer = &http.Server{ + //nolint:staticcheck Handler: h2c.NewHandler(n, &http2.Server{}), BaseContext: func(listener net.Listener) context.Context { return n.ctx diff --git a/protocol/tailscale/dns_transport.go b/protocol/tailscale/dns_transport.go index 08876ea9d..d91b498f1 100644 --- a/protocol/tailscale/dns_transport.go +++ b/protocol/tailscale/dns_transport.go @@ -27,6 +27,7 @@ import ( N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/service" nDNS "github.com/sagernet/tailscale/net/dns" + nDNSResolver "github.com/sagernet/tailscale/net/dns/resolver" "github.com/sagernet/tailscale/types/dnstype" "github.com/sagernet/tailscale/util/dnsname" "github.com/sagernet/tailscale/wgengine/router" @@ -55,6 +56,7 @@ type DNSTransport struct { routePrefixes []netip.Prefix routes map[string][]adapter.DNSTransport hosts map[string][]netip.Addr + magicHosts nDNSResolver.MagicDNSHosts searchDomains []string defaultResolvers []adapter.DNSTransport } @@ -150,6 +152,7 @@ func (t *DNSTransport) updateDNSServers(routeConfig *router.Config, dnsConfig *n t.routePrefixes = routePrefixes t.routes = routes t.hosts = hosts + t.magicHosts = t.endpoint.server.ExportLocalBackend().ExportMagicDNSHosts() t.searchDomains = searchDomains t.defaultResolvers = defaultResolvers t.access.Unlock() @@ -241,6 +244,7 @@ func (t *DNSTransport) Close() error { t.routePrefixes = nil t.routes = nil t.hosts = nil + t.magicHosts = nil t.defaultResolvers = nil t.access.Unlock() @@ -261,9 +265,14 @@ func (t *DNSTransport) Raw() bool { func (t *DNSTransport) PreferredDomain(domain string) bool { t.access.RLock() hosts := t.hosts + magicHosts := t.magicHosts routes := t.routes + searchDomains := t.searchDomains t.access.RUnlock() - if _, loaded := hosts[domain]; loaded { + if _, loaded := lookupHosts(hosts, magicHosts, domain); loaded { + return true + } + if t.acceptSearchDomain && len(searchDomains) > 0 && mDNS.CountLabel(domain) == 1 { return true } for suffix := range routes { @@ -351,30 +360,20 @@ func (t *DNSTransport) exchangeOnce(ctx context.Context, message *mDNS.Msg, allo t.access.RLock() hosts := t.hosts + magicHosts := t.magicHosts routes := t.routes defaultResolvers := t.defaultResolvers t.access.RUnlock() - addresses, hostsLoaded := hosts[question.Name] + addresses, hostsLoaded := lookupHosts(hosts, magicHosts, question.Name) if hostsLoaded { switch question.Qtype { - case mDNS.TypeA: - addresses4 := common.Filter(addresses, func(addr netip.Addr) bool { - return addr.Is4() - }) - if len(addresses4) > 0 { - callback(dns.FixedResponse(message.Id, question, addresses4, C.DefaultDNSTTL), nil) - return - } - case mDNS.TypeAAAA: - addresses6 := common.Filter(addresses, func(addr netip.Addr) bool { - return addr.Is6() - }) - if len(addresses6) > 0 { - callback(dns.FixedResponse(message.Id, question, addresses6, C.DefaultDNSTTL), nil) - return - } + case mDNS.TypeA, mDNS.TypeAAAA: + callback(dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil) + default: + callback(dns.FixedResponseStatus(message, mDNS.RcodeSuccess), nil) } + return } for domainSuffix, transports := range routes { if mDNS.IsSubDomain(domainSuffix, question.Name) { @@ -404,6 +403,30 @@ func (t *DNSTransport) exchangeOnce(ctx context.Context, message *mDNS.Msg, allo callback(nil, dns.RcodeNameError) } +func lookupHosts(hosts map[string][]netip.Addr, magicHosts nDNSResolver.MagicDNSHosts, name string) ([]netip.Addr, bool) { + addresses, loaded := hosts[name] + if loaded { + return addresses, true + } + if magicHosts == nil { + return nil, false + } + fqdn, err := dnsname.ToFQDN(name) + if err != nil { + return nil, false + } + addresses, loaded = magicHosts.LookupHost(fqdn) + if loaded { + return addresses, true + } + for parent := fqdn.Parent(); parent != ""; parent = parent.Parent() { + if magicHosts.SubdomainHost(parent) { + return magicHosts.LookupHost(parent) + } + } + return nil, false +} + func resolverExchangers(resolvers []adapter.DNSTransport, message *mDNS.Msg) []transport.AsyncExchanger { return common.Map(resolvers, func(resolver adapter.DNSTransport) transport.AsyncExchanger { return func(ctx context.Context, callback func(response *mDNS.Msg, err error)) { diff --git a/protocol/tailscale/endpoint.go b/protocol/tailscale/endpoint.go index cf5aea934..b7cce6f45 100644 --- a/protocol/tailscale/endpoint.go +++ b/protocol/tailscale/endpoint.go @@ -57,12 +57,10 @@ import ( "github.com/sagernet/tailscale/types/nettype" "github.com/sagernet/tailscale/version" "github.com/sagernet/tailscale/wgengine" - "github.com/sagernet/tailscale/wgengine/filter" "github.com/sagernet/tailscale/wgengine/router" "github.com/sagernet/tailscale/wgengine/wgcfg" mDNS "github.com/miekg/dns" - "go4.org/netipx" ) var ( @@ -92,7 +90,6 @@ type Endpoint struct { server *tsnet.Server stack *stack.Stack icmpForwarder *tun.ICMPForwarder - filter *atomic.Pointer[filter.Filter] returnAccess sync.Mutex returnPath tun.Return wgEngine wgengine.ExportedUserspaceEngine @@ -105,7 +102,6 @@ type Endpoint struct { routeDomains common.TypedValue[map[string]bool] routeSuffixes common.TypedValue[[]string] searchDomains atomic.Bool - routePrefixes atomic.Pointer[netipx.IPSet] acceptRoutes bool exitNode string @@ -330,7 +326,7 @@ func (t *Endpoint) start() error { } t.systemTun = systemTun t.systemDialer = systemDialer - t.server.TunDevice = wgTunDevice + t.server.Tun = wgTunDevice } if t.network.AutoRedirectOutputMark() != 0 { netns.SetControlFunc(t.network.AutoRedirectOutputMarkFunc()) @@ -467,12 +463,10 @@ func (t *Endpoint) postStart() error { t.logger.Warn("SSH server degraded: ", degraded) } } - localBackend := t.server.ExportLocalBackend() err = t.editPrefs(sshEnabled) if err != nil { return err } - t.filter = localBackend.ExportFilter() if sshEnabled { sshServer, err := tailssh.New(t.ctx, t.server, t.platformInterface, t.sshServerOptions, t.logger) if err != nil { @@ -494,48 +488,76 @@ func (t *Endpoint) watchState() { localBackend := t.server.ExportLocalBackend() var reportedAuthURL string exitNodePending := t.exitNode != "" - localBackend.WatchNotifications(t.ctx, ipn.NotifyInitialState, nil, func(roNotify *ipn.Notify) (keepGoing bool) { - if roNotify.State == nil && roNotify.BrowseToURL == nil { - return true + running := false + tryApplyExitNode := func() { + err := t.applyExitNode() + if err != nil { + t.logger.Error("set exit node: ", err) + } else { + exitNodePending = false } - status := localBackend.StatusWithoutPeers() - switch status.BackendState { - case ipn.NoState.String(), ipn.NeedsLogin.String(): - if t.exitNode != "" { - exitNodePending = true + } + for { + var busError string + localBackend.WatchNotifications(t.ctx, ipn.NotifyInitialState|ipn.NotifyPeerPatches, nil, func(roNotify *ipn.Notify) (keepGoing bool) { + if roNotify.ErrMessage != nil { + busError = *roNotify.ErrMessage + return false } - authURL := status.AuthURL - if authURL == "" || authURL == reportedAuthURL { + if running && exitNodePending && len(roNotify.PeersChanged) > 0 { + tryApplyExitNode() + } + if roNotify.State == nil && roNotify.BrowseToURL == nil { return true } - reportedAuthURL = authURL - t.logger.Info("Waiting for authentication: ", authURL) - if t.platformInterface != nil && t.platformInterface.UsePlatformNotification() { - err := t.platformInterface.SendNotification(&adapter.Notification{ - Identifier: "tailscale-authentication", - TypeName: "Tailscale Authentication Notifications", - TypeID: 10, - Title: "Tailscale Authentication", - Body: F.ToString("Tailscale outbound[", t.Tag(), "] is waiting for authentication."), - OpenURL: authURL, - }) - if err != nil { - t.logger.Error("send authentication notification: ", err) - } - } - case ipn.Running.String(): - reportedAuthURL = "" - if exitNodePending { - err := t.applyExitNode() - if err != nil { - t.logger.Error("set exit node: ", err) - } else { - exitNodePending = false + status := localBackend.StatusWithoutPeers() + running = status.BackendState == ipn.Running.String() + switch status.BackendState { + case ipn.NoState.String(), ipn.NeedsLogin.String(): + if t.exitNode != "" { + exitNodePending = true + } + authURL := status.AuthURL + if authURL == "" || authURL == reportedAuthURL { + return true + } + reportedAuthURL = authURL + t.logger.Info("Waiting for authentication: ", authURL) + if t.platformInterface != nil && t.platformInterface.UsePlatformNotification() { + err := t.platformInterface.SendNotification(&adapter.Notification{ + Identifier: "tailscale-authentication", + TypeName: "Tailscale Authentication Notifications", + TypeID: 10, + Title: "Tailscale Authentication", + Body: F.ToString("Tailscale outbound[", t.Tag(), "] is waiting for authentication."), + OpenURL: authURL, + }) + if err != nil { + t.logger.Error("send authentication notification: ", err) + } + } + case ipn.Running.String(): + reportedAuthURL = "" + if exitNodePending { + tryApplyExitNode() } } + return true + }) + if t.ctx.Err() != nil { + return } - return true - }) + if busError != "" { + t.logger.Warn("restarting state watcher: ", busError) + } else { + t.logger.Warn("state watcher stopped unexpectedly, restarting") + } + select { + case <-t.ctx.Done(): + return + case <-time.After(time.Second): + } + } } func (t *Endpoint) editPrefs(sshEnabled bool) error { @@ -895,6 +917,12 @@ func (t *Endpoint) PreferredDomain(metadata *adapter.InboundContext, domain stri if routeDomains[domain] { return true } + if t.started.Load() { + magicHosts := t.server.ExportLocalBackend().ExportMagicDNSHosts() + if _, found := lookupHosts(nil, magicHosts, domain); found { + return true + } + } for _, suffix := range t.routeSuffixes.Load() { if mDNS.IsSubDomain(suffix, domain) { return true @@ -904,11 +932,11 @@ func (t *Endpoint) PreferredDomain(metadata *adapter.InboundContext, domain stri } func (t *Endpoint) PreferredAddress(metadata *adapter.InboundContext, address netip.Addr) bool { - routePrefixes := t.routePrefixes.Load() - if routePrefixes == nil { + if !t.started.Load() { return false } - return routePrefixes.Contains(address) + peer, found := t.server.ExportLocalBackend().PeerForIP(address) + return found && !peer.IsSelf && peer.Route.Bits() > 0 } func (t *Endpoint) Server() *tsnet.Server { @@ -919,6 +947,12 @@ func (t *Endpoint) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCf if cfg == nil || dnsCfg == nil { return } + // The engine invokes the listener on every Reconfig call, including + // unchanged ones: SSH policy lives only in the netmap, outside the + // three configs, so the SSH hook must run before the change check. + if t.sshReconfigHook != nil { + t.sshReconfigHook(cfg, routerCfg, dnsCfg) + } if t.cfg != nil && reflect.DeepEqual(t.cfg, cfg) && t.routerCfg != nil && reflect.DeepEqual(t.routerCfg, routerCfg) && t.dnsCfg != nil && reflect.DeepEqual(t.dnsCfg, dnsCfg) { @@ -943,23 +977,9 @@ func (t *Endpoint) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCf t.routeSuffixes.Store(routeSuffixes) t.searchDomains.Store(len(dnsCfg.SearchDomains) > 0) - var builder netipx.IPSetBuilder - for _, peer := range cfg.Peers { - for _, allowedIP := range peer.AllowedIPs { - if allowedIP.Bits() == 0 { - continue - } - builder.AddPrefix(allowedIP) - } - } - t.routePrefixes.Store(common.Must1(builder.IPSet())) - if t.onReconfigHook != nil { t.onReconfigHook(cfg, routerCfg, dnsCfg) } - if t.sshReconfigHook != nil { - t.sshReconfigHook(cfg, routerCfg, dnsCfg) - } } func addressFromAddr(destination netip.Addr) tcpip.Address { diff --git a/protocol/tailscale/port.go b/protocol/tailscale/port.go index ae0c2b9a5..39f8dd384 100644 --- a/protocol/tailscale/port.go +++ b/protocol/tailscale/port.go @@ -41,8 +41,8 @@ func (t *Endpoint) JudgeFlow(network uint8, source netip.AddrPort, destination n if destination.Addr() == inet4Address || destination.Addr() == inet6Address { return tun.FlowVerdict{Action: tun.ActionAccept} } - if t.filter != nil { - tsFilter := t.filter.Load() + if t.started.Load() { + tsFilter := t.wgEngine.GetFilter() if tsFilter != nil { var ( ipProto ipproto.Proto diff --git a/protocol/tailscale/status.go b/protocol/tailscale/status.go index 94b423355..62f3b5324 100644 --- a/protocol/tailscale/status.go +++ b/protocol/tailscale/status.go @@ -5,6 +5,7 @@ package tailscale import ( "context" "slices" + "time" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/tailscale/ipn" @@ -15,25 +16,60 @@ var _ adapter.TailscaleEndpoint = (*Endpoint)(nil) func (t *Endpoint) SubscribeTailscaleStatus(ctx context.Context, fn func(*adapter.TailscaleEndpointStatus)) error { localBackend := t.server.ExportLocalBackend() - sendStatus := func() { - status := localBackend.Status() - result := convertTailscaleStatus(status) - result.KeyAuth = t.keyAuth - fn(result) - } - sendStatus() - localBackend.WatchNotifications(ctx, ipn.NotifyInitialState|ipn.NotifyInitialNetMap|ipn.NotifyRateLimit, nil, func(roNotify *ipn.Notify) (keepGoing bool) { + // The notification callback must stay cheap and non-blocking: a + // watcher whose queue fills is disconnected by the IPN bus, so + // status collection and delivery (which blocks on the subscriber) + // run on a separate coalescing goroutine. + updateSignal := make(chan struct{}, 1) + scheduleUpdate := func() { select { - case <-ctx.Done(): - return false + case updateSignal <- struct{}{}: default: } - if roNotify.State != nil || roNotify.NetMap != nil || roNotify.BrowseToURL != nil || roNotify.Prefs != nil { - sendStatus() + } + go func() { + for { + select { + case <-ctx.Done(): + return + case <-updateSignal: + } + status := localBackend.Status() + result := convertTailscaleStatus(status) + result.KeyAuth = t.keyAuth + fn(result) } - return true - }) - return ctx.Err() + }() + scheduleUpdate() + for { + var busError string + localBackend.WatchNotifications(ctx, ipn.NotifyInitialState|ipn.NotifyPeerPatches, nil, func(roNotify *ipn.Notify) (keepGoing bool) { + if roNotify.ErrMessage != nil { + busError = *roNotify.ErrMessage + return false + } + if roNotify.State != nil || roNotify.SelfChange != nil || + len(roNotify.PeersChanged) > 0 || len(roNotify.PeersRemoved) > 0 || len(roNotify.PeerChangedPatch) > 0 || + roNotify.BrowseToURL != nil || roNotify.Prefs != nil { + scheduleUpdate() + } + return true + }) + if ctx.Err() != nil { + return ctx.Err() + } + if busError != "" { + t.logger.Warn("restarting status watcher: ", busError) + } else { + t.logger.Warn("status watcher stopped unexpectedly, restarting") + } + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(time.Second): + } + scheduleUpdate() + } } func convertTailscaleStatus(status *ipnstate.Status) *adapter.TailscaleEndpointStatus { @@ -78,25 +114,27 @@ func convertTailscaleStatus(status *ipnstate.Status) *adapter.TailscaleEndpointS return 0 }) } - if status.ExitNodeStatus != nil { - for _, peerKey := range status.Peers() { - peer := status.Peer[peerKey] - if peer.ID == status.ExitNodeStatus.ID { - result.ExitNode = convertTailscalePeer(peer) - break - } + // status.ExitNodeStatus is populated from the cached netmap Peers + // slice, which incremental deltas do not update; the live peer map + // behind status.Peer sets PeerStatus.ExitNode delta-correctly, so it + // is the primary source. + for _, peerKey := range status.Peers() { + peer := status.Peer[peerKey] + if peer.ExitNode { + result.ExitNode = convertTailscalePeer(peer) + break } - if result.ExitNode == nil { - ips := make([]string, 0, len(status.ExitNodeStatus.TailscaleIPs)) - for _, prefix := range status.ExitNodeStatus.TailscaleIPs { - ips = append(ips, prefix.Addr().String()) - } - result.ExitNode = &adapter.TailscalePeer{ - StableID: string(status.ExitNodeStatus.ID), - TailscaleIPs: ips, - Online: status.ExitNodeStatus.Online, - ExitNode: true, - } + } + if result.ExitNode == nil && status.ExitNodeStatus != nil { + ips := make([]string, 0, len(status.ExitNodeStatus.TailscaleIPs)) + for _, prefix := range status.ExitNodeStatus.TailscaleIPs { + ips = append(ips, prefix.Addr().String()) + } + result.ExitNode = &adapter.TailscalePeer{ + StableID: string(status.ExitNodeStatus.ID), + TailscaleIPs: ips, + Online: status.ExitNodeStatus.Online, + ExitNode: true, } } return result diff --git a/protocol/tailscale/tailssh/server.go b/protocol/tailscale/tailssh/server.go index 7fa93ae84..27dc9d251 100644 --- a/protocol/tailscale/tailssh/server.go +++ b/protocol/tailscale/tailssh/server.go @@ -287,7 +287,7 @@ func (s *Server) authenticate(ctx gliderssh.Context, conn gossh.ConnMetadata) (* s.logger.Warn("SSH auth: unknown peer ", remoteAddrPort) return nil, &gossh.PartialSuccessError{} } - netMap := localBackend.NetMap() + netMap := localBackend.NetMapNoPeers() if netMap == nil || netMap.SSHPolicy == nil { s.logger.Warn("SSH auth: no SSH policy") return nil, &gossh.PartialSuccessError{} @@ -419,7 +419,7 @@ func (s *Server) holdAndDelegate(ctx context.Context, action *tailcfg.SSHAction, srcNodeIP = node.Addresses().At(0).Addr() } var dstNodeID string - netMap := lb.NetMap() + netMap := lb.NetMapNoPeers() if netMap != nil && netMap.SelfNode.Valid() { dstNodeID = fmt.Sprint(int64(netMap.SelfNode.ID())) } @@ -831,7 +831,7 @@ func (s *Server) buildEnvironment(session gliderssh.Session, connInfo *sshConnIn // capability, matching upstream's capability gate. acceptEnv := connInfo.acceptEnv if len(acceptEnv) > 0 { - netMap := s.tsnetServer.ExportLocalBackend().NetMap() + netMap := s.tsnetServer.ExportLocalBackend().NetMapNoPeers() if netMap == nil || !netMap.HasCap(tailcfg.NodeAttrSSHEnvironmentVariables) { acceptEnv = nil } @@ -957,7 +957,7 @@ func (s *Server) allowReverseUnixForward(ctx gliderssh.Context, socketPath strin func (s *Server) OnReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCfg *tsDNS.Config) { localBackend := s.tsnetServer.ExportLocalBackend() - netMap := localBackend.NetMap() + netMap := localBackend.NetMapNoPeers() if netMap == nil || netMap.SSHPolicy == nil { return } diff --git a/service/api/server.go b/service/api/server.go index 40cd960c1..658e65642 100644 --- a/service/api/server.go +++ b/service/api/server.go @@ -19,7 +19,7 @@ import ( aTLS "github.com/sagernet/sing/common/tls" "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" + "golang.org/x/net/http2/h2c" //nolint:staticcheck "google.golang.org/grpc" ) @@ -83,6 +83,7 @@ func (s *Service) Start(stage adapter.StartStage) error { } } s.httpServer = &http.Server{ + //nolint:staticcheck Handler: h2c.NewHandler(newHTTPHandler(s.logger, s.grpcServer, s.options, s.dashboard), new(http2.Server)), BaseContext: func(net.Listener) context.Context { return s.ctx diff --git a/service/derp/service.go b/service/derp/service.go index a754580c0..e251a799e 100644 --- a/service/derp/service.go +++ b/service/derp/service.go @@ -49,7 +49,7 @@ import ( "github.com/coder/websocket" "github.com/go-chi/render" "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" + "golang.org/x/net/http2/h2c" //nolint:staticcheck ) func Register(registry *boxService.Registry) { @@ -217,6 +217,7 @@ func (d *Service) Start(stage adapter.StartStage) error { } tcpListener = aTLS.NewListener(tcpListener, d.tlsConfig) httpServer := &http.Server{ + //nolint:staticcheck Handler: h2c.NewHandler(derpMux, &http2.Server{}), } go httpServer.Serve(tcpListener) diff --git a/transport/v2raygrpclite/server.go b/transport/v2raygrpclite/server.go index 622d785ab..6dd04ce1c 100644 --- a/transport/v2raygrpclite/server.go +++ b/transport/v2raygrpclite/server.go @@ -22,7 +22,7 @@ import ( sHttp "github.com/sagernet/sing/protocol/http" "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" + "golang.org/x/net/http2/h2c" //nolint:staticcheck ) var _ adapter.V2RayServerTransport = (*Server)(nil) @@ -56,6 +56,7 @@ func NewServer(ctx context.Context, logger logger.ContextLogger, options option. return log.ContextWithNewID(ctx) }, } + //nolint:staticcheck server.h2cHandler = h2c.NewHandler(server, server.h2Server) return server, nil } diff --git a/transport/v2rayhttp/server.go b/transport/v2rayhttp/server.go index 282c7c239..de03c40ca 100644 --- a/transport/v2rayhttp/server.go +++ b/transport/v2rayhttp/server.go @@ -24,7 +24,7 @@ import ( sHttp "github.com/sagernet/sing/protocol/http" "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" + "golang.org/x/net/http2/h2c" //nolint:staticcheck ) var _ adapter.V2RayServerTransport = (*Server)(nil) @@ -71,6 +71,7 @@ func NewServer(ctx context.Context, logger logger.ContextLogger, options option. return log.ContextWithNewID(ctx) }, } + //nolint:staticcheck server.h2cHandler = h2c.NewHandler(server, server.h2Server) return server, nil } diff --git a/transport/wireguard/endpoint.go b/transport/wireguard/endpoint.go index 8b2e87ee1..243baa40f 100644 --- a/transport/wireguard/endpoint.go +++ b/transport/wireguard/endpoint.go @@ -257,7 +257,7 @@ func (e *Endpoint) Lookup(address netip.Addr) *device.Peer { if e.allowedIPs == nil { return nil } - return e.allowedIPs.Lookup(address.AsSlice()) + return e.allowedIPs.LookupFromPacket(netip.Addr{}, address, nil) } func (e *Endpoint) BindUpdate() error {