Update tailscale to v1.102.1

This commit is contained in:
世界 2026-08-04 20:33:23 +08:00
parent 9bf39f85f9
commit e797c2d0ba
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
18 changed files with 282 additions and 210 deletions

View file

@ -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) {

View file

@ -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()

View file

@ -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)

View file

@ -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() {

33
go.mod
View file

@ -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

71
go.sum
View file

@ -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=

View file

@ -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),

View file

@ -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

View file

@ -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)) {

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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

View file

@ -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)

View file

@ -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
}

View file

@ -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
}

View file

@ -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 {