diff --git a/.github/setup_legacy_go.sh b/.github/setup_go_for_windows7.sh similarity index 51% rename from .github/setup_legacy_go.sh rename to .github/setup_go_for_windows7.sh index d4617e79..2344453f 100755 --- a/.github/setup_legacy_go.sh +++ b/.github/setup_go_for_windows7.sh @@ -1,25 +1,27 @@ #!/usr/bin/env bash -VERSION="1.23.12" +VERSION="1.25.3" mkdir -p $HOME/go cd $HOME/go wget "https://dl.google.com/go/go${VERSION}.linux-amd64.tar.gz" tar -xzf "go${VERSION}.linux-amd64.tar.gz" -mv go go_legacy -cd go_legacy +mv go go_win7 +cd go_win7 # modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557 -# this patch file only works on golang1.23.x -# that means after golang1.24 release it must be changed -# see: https://github.com/MetaCubeX/go/commits/release-branch.go1.23/ +# this patch file only works on golang1.25.x +# that means after golang1.26 release it must be changed +# see: https://github.com/MetaCubeX/go/commits/release-branch.go1.25/ # revert: # 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng" # 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7" # 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround" # a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries" -curl https://github.com/MetaCubeX/go/commit/9ac42137ef6730e8b7daca016ece831297a1d75b.diff | patch --verbose -p 1 -curl https://github.com/MetaCubeX/go/commit/21290de8a4c91408de7c2b5b68757b1e90af49dd.diff | patch --verbose -p 1 -curl https://github.com/MetaCubeX/go/commit/6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76.diff | patch --verbose -p 1 -curl https://github.com/MetaCubeX/go/commit/69e2eed6dd0f6d815ebf15797761c13f31213dd6.diff | patch --verbose -p 1 +alias curl='curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"' + +curl https://github.com/MetaCubeX/go/commit/8cb5472d94c34b88733a81091bd328e70ee565a4.diff | patch --verbose -p 1 +curl https://github.com/MetaCubeX/go/commit/6788c4c6f9fafb56729bad6b660f7ee2272d699f.diff | patch --verbose -p 1 +curl https://github.com/MetaCubeX/go/commit/a5b2168bb836ed9d6601c626f95e56c07923f906.diff | patch --verbose -p 1 +curl https://github.com/MetaCubeX/go/commit/f56f1e23507e646c85243a71bde7b9629b2f970c.diff | patch --verbose -p 1 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a15c75ca..7bd16108 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,7 +46,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.25.1 + go-version: ^1.25.3 - name: Check input version if: github.event_name == 'workflow_dispatch' run: |- @@ -88,9 +88,9 @@ jobs: - { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" } - { os: windows, arch: amd64 } - - { os: windows, arch: amd64, legacy_go123: true, legacy_name: "windows-7" } + - { os: windows, arch: amd64, legacy_win7: true, legacy_name: "windows-7" } - { os: windows, arch: "386" } - - { os: windows, arch: "386", legacy_go123: true, legacy_name: "windows-7" } + - { os: windows, arch: "386", legacy_win7: true, legacy_name: "windows-7" } - { os: windows, arch: arm64 } - { os: darwin, arch: amd64 } @@ -110,29 +110,29 @@ jobs: if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }} uses: actions/setup-go@v5 with: - go-version: ^1.25.1 + go-version: ^1.25.3 - name: Setup Go 1.24 if: matrix.legacy_go124 uses: actions/setup-go@v5 with: go-version: ~1.24.6 - - name: Cache Go 1.23 - if: matrix.legacy_go123 - id: cache-legacy-go + - name: Cache Go for Windows 7 + if: matrix.legacy_win7 + id: cache-go-for-windows7 uses: actions/cache@v4 with: path: | - ~/go/go_legacy - key: go_legacy_12312 - - name: Setup Go 1.23 - if: matrix.legacy_go123 && steps.cache-legacy-go.outputs.cache-hit != 'true' + ~/go/go_win7 + key: go_win7_1253 + - name: Setup Go for Windows 7 + if: matrix.legacy_win7 && steps.cache-go-for-windows7.outputs.cache-hit != 'true' run: |- - .github/setup_legacy_go.sh - - name: Setup Go 1.23 - if: matrix.legacy_go123 + .github/setup_go_for_windows7.sh + - name: Setup Go for Windows 7 + if: matrix.legacy_win7 run: |- - echo "PATH=$HOME/go/go_legacy/bin:$PATH" >> $GITHUB_ENV - echo "GOROOT=$HOME/go/go_legacy" >> $GITHUB_ENV + echo "PATH=$HOME/go/go_win7/bin:$PATH" >> $GITHUB_ENV + echo "GOROOT=$HOME/go/go_win7" >> $GITHUB_ENV - name: Setup Android NDK if: matrix.os == 'android' uses: nttld/setup-ndk@v1 @@ -300,7 +300,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.25.1 + go-version: ^1.25.3 - name: Setup Android NDK id: setup-ndk uses: nttld/setup-ndk@v1 @@ -380,7 +380,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.25.1 + go-version: ^1.25.3 - name: Setup Android NDK id: setup-ndk uses: nttld/setup-ndk@v1 @@ -479,7 +479,7 @@ jobs: if: matrix.if uses: actions/setup-go@v5 with: - go-version: ^1.25.1 + go-version: ^1.25.3 - name: Set tag if: matrix.if run: |- diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0a8cbd1a..214b684d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -32,7 +32,7 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v8 with: - version: latest + version: v2.4.0 args: --timeout=30m install-mode: binary verify: false diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index b43cfb39..b6c9e916 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -30,7 +30,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.25.1 + go-version: ^1.25.3 - name: Check input version if: github.event_name == 'workflow_dispatch' run: |- @@ -71,7 +71,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ^1.25.1 + go-version: ^1.25.3 - name: Setup Android NDK if: matrix.os == 'android' uses: nttld/setup-ndk@v1 diff --git a/.gitignore b/.gitignore index 60eb851e..5a964b24 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,6 @@ .DS_Store /config.d/ /venv/ - +CLAUDE.md +AGENTS.md +/.claude/ diff --git a/Makefile b/Makefile index a98faeac..0f78baaf 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ fmt: @gci write --custom-order -s standard -s "prefix(github.com/sagernet/)" -s "default" . fmt_install: - go install -v mvdan.cc/gofumpt@latest + go install -v mvdan.cc/gofumpt@v0.8.0 go install -v github.com/daixiang0/gci@latest lint: @@ -49,7 +49,7 @@ lint: GOOS=freebsd golangci-lint run ./... lint_install: - go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest + go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.4.0 proto: @go run ./cmd/internal/protogen diff --git a/clients/android b/clients/android index cd8ac376..a4e3c00f 160000 --- a/clients/android +++ b/clients/android @@ -1 +1 @@ -Subproject commit cd8ac376f66c2dac3f6d7c9b2114fc2ead405acc +Subproject commit a4e3c00f0fbffd065be8ec461171d6777f62ccc0 diff --git a/clients/apple b/clients/apple index 96bee16b..84d8cf17 160000 --- a/clients/apple +++ b/clients/apple @@ -1 +1 @@ -Subproject commit 96bee16bf04eb3216f1a99bcf02224b012511182 +Subproject commit 84d8cf17574b90bb005e494c723c2d326f53c7d1 diff --git a/cmd/internal/app_store_connect/main.go b/cmd/internal/app_store_connect/main.go index d8e30f2a..1e7109b6 100644 --- a/cmd/internal/app_store_connect/main.go +++ b/cmd/internal/app_store_connect/main.go @@ -134,6 +134,7 @@ func publishTestflight(ctx context.Context) error { asc.PlatformTVOS, } } + waitingForProcess := false for _, platform := range platforms { log.Info(string(platform), " list builds") for { @@ -145,12 +146,13 @@ func publishTestflight(ctx context.Context) error { return err } build := builds.Data[0] - if common.Contains(buildIDs, build.ID) || time.Since(build.Attributes.UploadedDate.Time) > 30*time.Minute { + if !waitingForProcess && (common.Contains(buildIDs, build.ID) || time.Since(build.Attributes.UploadedDate.Time) > 30*time.Minute) { log.Info(string(platform), " ", tag, " waiting for process") time.Sleep(15 * time.Second) continue } if *build.Attributes.ProcessingState != "VALID" { + waitingForProcess = true log.Info(string(platform), " ", tag, " waiting for process: ", *build.Attributes.ProcessingState) time.Sleep(15 * time.Second) continue diff --git a/common/tls/client.go b/common/tls/client.go index 5e05c990..afdb5a42 100644 --- a/common/tls/client.go +++ b/common/tls/client.go @@ -53,26 +53,48 @@ func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, e return tlsConn, nil } -type Dialer struct { +type Dialer interface { + N.Dialer + DialTLSContext(ctx context.Context, destination M.Socksaddr) (Conn, error) +} + +type defaultDialer struct { dialer N.Dialer config Config } -func NewDialer(dialer N.Dialer, config Config) N.Dialer { - return &Dialer{dialer, config} +func NewDialer(dialer N.Dialer, config Config) Dialer { + return &defaultDialer{dialer, config} } -func (d *Dialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - if network != N.NetworkTCP { +func (d *defaultDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + if N.NetworkName(network) != N.NetworkTCP { return nil, os.ErrInvalid } - conn, err := d.dialer.DialContext(ctx, network, destination) + return d.DialTLSContext(ctx, destination) +} + +func (d *defaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + return nil, os.ErrInvalid +} + +func (d *defaultDialer) DialTLSContext(ctx context.Context, destination M.Socksaddr) (Conn, error) { + return d.dialContext(ctx, destination) +} + +func (d *defaultDialer) dialContext(ctx context.Context, destination M.Socksaddr) (Conn, error) { + conn, err := d.dialer.DialContext(ctx, N.NetworkTCP, destination) if err != nil { return nil, err } - return ClientHandshake(ctx, conn, d.config) + tlsConn, err := aTLS.ClientHandshake(ctx, conn, d.config) + if err != nil { + conn.Close() + return nil, err + } + return tlsConn, nil } -func (d *Dialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - return nil, os.ErrInvalid +func (d *defaultDialer) Upstream() any { + return d.dialer } diff --git a/dns/client.go b/dns/client.go index 585c541e..8db45d41 100644 --- a/dns/client.go +++ b/dns/client.go @@ -95,6 +95,20 @@ func (c *Client) Start() { } } +func extractNegativeTTL(response *dns.Msg) (uint32, bool) { + for _, record := range response.Ns { + if soa, isSOA := record.(*dns.SOA); isSOA { + soaTTL := soa.Header().Ttl + soaMinimum := soa.Minttl + if soaTTL < soaMinimum { + return soaTTL, true + } + return soaMinimum, true + } + } + return 0, false +} + func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, message *dns.Msg, options adapter.DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) (*dns.Msg, error) { if len(message.Question) == 0 { if c.logger != nil { @@ -214,7 +228,7 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m response.Answer = append(response.Answer, validResponse.Answer...) } }*/ - disableCache = disableCache || response.Rcode != dns.RcodeSuccess || len(response.Answer) == 0 + disableCache = disableCache || (response.Rcode != dns.RcodeSuccess && response.Rcode != dns.RcodeNameError) if responseChecker != nil { var rejected bool // TODO: add accept_any rule and support to check response instead of addresses @@ -251,10 +265,17 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m } } var timeToLive uint32 - for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} { - for _, record := range recordList { - if timeToLive == 0 || record.Header().Ttl > 0 && record.Header().Ttl < timeToLive { - timeToLive = record.Header().Ttl + if len(response.Answer) == 0 { + if soaTTL, hasSOA := extractNegativeTTL(response); hasSOA { + timeToLive = soaTTL + } + } + if timeToLive == 0 { + for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} { + for _, record := range recordList { + if timeToLive == 0 || record.Header().Ttl > 0 && record.Header().Ttl < timeToLive { + timeToLive = record.Header().Ttl + } } } } @@ -364,14 +385,18 @@ func (c *Client) LookupCache(domain string, strategy C.DomainStrategy) ([]netip. Qtype: dns.TypeA, Qclass: dns.ClassINET, }, nil) + if response4 == nil { + return nil, false + } response6, _ := c.loadResponse(dns.Question{ Name: dnsName, Qtype: dns.TypeAAAA, Qclass: dns.ClassINET, }, nil) - if response4 != nil || response6 != nil { - return sortAddresses(MessageToAddresses(response4), MessageToAddresses(response6), strategy), true + if response6 == nil { + return nil, false } + return sortAddresses(MessageToAddresses(response4), MessageToAddresses(response6), strategy), true } return nil, false } diff --git a/dns/client_truncate.go b/dns/client_truncate.go index f00023f2..19165f99 100644 --- a/dns/client_truncate.go +++ b/dns/client_truncate.go @@ -15,8 +15,7 @@ func TruncateDNSMessage(request *dns.Msg, response *dns.Msg, headroom int) (*buf } responseLen := response.Len() if responseLen > maxLen { - copyResponse := *response - response = ©Response + response = response.Copy() response.Truncate(maxLen) } buffer := buf.NewSize(headroom*2 + 1 + responseLen) diff --git a/dns/router.go b/dns/router.go index e62f0ef5..ae9e68c7 100644 --- a/dns/router.go +++ b/dns/router.go @@ -386,12 +386,7 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ if rule != nil { switch action := rule.Action().(type) { case *R.RuleActionReject: - switch action.Method { - case C.RuleActionRejectMethodDefault: - return nil, nil - case C.RuleActionRejectMethodDrop: - return nil, tun.ErrDrop - } + return nil, &R.RejectedError{Cause: action.Error(ctx)} case *R.RuleActionPredefined: if action.Rcode != mDNS.RcodeSuccess { err = RcodeError(action.Rcode) diff --git a/dns/transport/https.go b/dns/transport/https.go index 7d56f45e..30c2a11f 100644 --- a/dns/transport/https.go +++ b/dns/transport/https.go @@ -25,7 +25,6 @@ import ( "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" - aTLS "github.com/sagernet/sing/common/tls" sHTTP "github.com/sagernet/sing/protocol/http" mDNS "github.com/miekg/dns" @@ -47,7 +46,7 @@ type HTTPSTransport struct { destination *url.URL headers http.Header transportAccess sync.Mutex - transport *http.Transport + transport *HTTPSTransportWrapper transportResetAt time.Time } @@ -62,11 +61,8 @@ func NewHTTPS(ctx context.Context, logger log.ContextLogger, tag string, options if err != nil { return nil, err } - if common.Error(tlsConfig.Config()) == nil && !common.Contains(tlsConfig.NextProtos(), http2.NextProtoTLS) { - tlsConfig.SetNextProtos(append(tlsConfig.NextProtos(), http2.NextProtoTLS)) - } - if !common.Contains(tlsConfig.NextProtos(), "http/1.1") { - tlsConfig.SetNextProtos(append(tlsConfig.NextProtos(), "http/1.1")) + if len(tlsConfig.NextProtos()) == 0 { + tlsConfig.SetNextProtos([]string{http2.NextProtoTLS, "http/1.1"}) } headers := options.Headers.Build() host := headers.Get("Host") @@ -124,37 +120,13 @@ func NewHTTPSRaw( serverAddr M.Socksaddr, tlsConfig tls.Config, ) *HTTPSTransport { - var transport *http.Transport - if tlsConfig != nil { - transport = &http.Transport{ - ForceAttemptHTTP2: true, - DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - tcpConn, hErr := dialer.DialContext(ctx, network, serverAddr) - if hErr != nil { - return nil, hErr - } - tlsConn, hErr := aTLS.ClientHandshake(ctx, tcpConn, tlsConfig) - if hErr != nil { - tcpConn.Close() - return nil, hErr - } - return tlsConn, nil - }, - } - } else { - transport = &http.Transport{ - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - return dialer.DialContext(ctx, network, serverAddr) - }, - } - } return &HTTPSTransport{ TransportAdapter: adapter, logger: logger, dialer: dialer, destination: destination, headers: headers, - transport: transport, + transport: NewHTTPSTransportWrapper(tls.NewDialer(dialer, tlsConfig), serverAddr), } } diff --git a/dns/transport/https_transport.go b/dns/transport/https_transport.go new file mode 100644 index 00000000..84cfa17c --- /dev/null +++ b/dns/transport/https_transport.go @@ -0,0 +1,80 @@ +package transport + +import ( + "context" + "errors" + "net" + "net/http" + "sync/atomic" + + "github.com/sagernet/sing-box/common/tls" + E "github.com/sagernet/sing/common/exceptions" + M "github.com/sagernet/sing/common/metadata" + + "golang.org/x/net/http2" +) + +var errFallback = E.New("fallback to HTTP/1.1") + +type HTTPSTransportWrapper struct { + http2Transport *http2.Transport + httpTransport *http.Transport + fallback *atomic.Bool +} + +func NewHTTPSTransportWrapper(dialer tls.Dialer, serverAddr M.Socksaddr) *HTTPSTransportWrapper { + var fallback atomic.Bool + return &HTTPSTransportWrapper{ + http2Transport: &http2.Transport{ + DialTLSContext: func(ctx context.Context, _, _ string, _ *tls.STDConfig) (net.Conn, error) { + tlsConn, err := dialer.DialTLSContext(ctx, serverAddr) + if err != nil { + return nil, err + } + state := tlsConn.ConnectionState() + if state.NegotiatedProtocol == http2.NextProtoTLS { + return tlsConn, nil + } + tlsConn.Close() + fallback.Store(true) + return nil, errFallback + }, + }, + httpTransport: &http.Transport{ + DialTLSContext: func(ctx context.Context, _, _ string) (net.Conn, error) { + return dialer.DialTLSContext(ctx, serverAddr) + }, + }, + fallback: &fallback, + } +} + +func (h *HTTPSTransportWrapper) RoundTrip(request *http.Request) (*http.Response, error) { + if h.fallback.Load() { + return h.httpTransport.RoundTrip(request) + } else { + response, err := h.http2Transport.RoundTrip(request) + if err != nil { + if errors.Is(err, errFallback) { + return h.httpTransport.RoundTrip(request) + } + return nil, err + } + return response, nil + } +} + +func (h *HTTPSTransportWrapper) CloseIdleConnections() { + h.http2Transport.CloseIdleConnections() + h.httpTransport.CloseIdleConnections() +} + +func (h *HTTPSTransportWrapper) Clone() *HTTPSTransportWrapper { + return &HTTPSTransportWrapper{ + httpTransport: h.httpTransport, + http2Transport: &http2.Transport{ + DialTLSContext: h.http2Transport.DialTLSContext, + }, + fallback: h.fallback, + } +} diff --git a/dns/transport/local/local.go b/dns/transport/local/local.go index 4e53586d..51badec4 100644 --- a/dns/transport/local/local.go +++ b/dns/transport/local/local.go @@ -54,18 +54,17 @@ func (t *Transport) Close() error { func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { question := message.Question[0] - domain := dns.FqdnToDomain(question.Name) if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA { - addresses := t.hosts.Lookup(domain) + addresses := t.hosts.Lookup(dns.FqdnToDomain(question.Name)) if len(addresses) > 0 { return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil } } systemConfig := getSystemDNSConfig(t.ctx) if systemConfig.singleRequest || !(message.Question[0].Qtype == mDNS.TypeA || message.Question[0].Qtype == mDNS.TypeAAAA) { - return t.exchangeSingleRequest(ctx, systemConfig, message, domain) + return t.exchangeSingleRequest(ctx, systemConfig, message, question.Name) } else { - return t.exchangeParallel(ctx, systemConfig, message, domain) + return t.exchangeParallel(ctx, systemConfig, message, question.Name) } } diff --git a/dns/transport/local/local_fallback.go b/dns/transport/local/local_fallback.go index cd2d198f..1e7e0238 100644 --- a/dns/transport/local/local_fallback.go +++ b/dns/transport/local/local_fallback.go @@ -67,7 +67,6 @@ func (f *FallbackTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*m return f.DNSTransport.Exchange(ctx, message) } question := message.Question[0] - domain := dns.FqdnToDomain(question.Name) if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA { var network string if question.Qtype == mDNS.TypeA { @@ -75,7 +74,7 @@ func (f *FallbackTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*m } else { network = "ip6" } - addresses, err := f.resolver.LookupNetIP(ctx, network, domain) + addresses, err := f.resolver.LookupNetIP(ctx, network, question.Name) if err != nil { var dnsError *net.DNSError if errors.As(err, &dnsError) && dnsError.IsNotFound { @@ -85,7 +84,7 @@ func (f *FallbackTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*m } return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil } else if question.Qtype == mDNS.TypeNS { - records, err := f.resolver.LookupNS(ctx, domain) + records, err := f.resolver.LookupNS(ctx, question.Name) if err != nil { var dnsError *net.DNSError if errors.As(err, &dnsError) && dnsError.IsNotFound { @@ -114,7 +113,7 @@ func (f *FallbackTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*m } return response, nil } else if question.Qtype == mDNS.TypeCNAME { - cname, err := f.resolver.LookupCNAME(ctx, domain) + cname, err := f.resolver.LookupCNAME(ctx, question.Name) if err != nil { var dnsError *net.DNSError if errors.As(err, &dnsError) && dnsError.IsNotFound { @@ -142,7 +141,7 @@ func (f *FallbackTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*m }, }, nil } else if question.Qtype == mDNS.TypeTXT { - records, err := f.resolver.LookupTXT(ctx, domain) + records, err := f.resolver.LookupTXT(ctx, question.Name) if err != nil { var dnsError *net.DNSError if errors.As(err, &dnsError) && dnsError.IsNotFound { @@ -170,7 +169,7 @@ func (f *FallbackTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*m }, }, nil } else if question.Qtype == mDNS.TypeMX { - records, err := f.resolver.LookupMX(ctx, domain) + records, err := f.resolver.LookupMX(ctx, question.Name) if err != nil { var dnsError *net.DNSError if errors.As(err, &dnsError) && dnsError.IsNotFound { diff --git a/docs/changelog.md b/docs/changelog.md index 73e4d04a..adb44dfe 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,6 +2,28 @@ icon: material/alert-decagram --- +#### 1.12.12 + +* Fixes and improvements + +#### 1.12.11 + +* Fixes and improvements + +#### 1.12.10 + +* Update uTLS to v1.8.1 **1** +* Fixes and improvements + +**1**: + +This update fixes an critical issue that could cause simulated Chrome fingerprints to be detected, +see https://github.com/refraction-networking/utls/pull/375. + +#### 1.12.9 + +* Fixes and improvements + #### 1.12.8 * Fixes and improvements @@ -92,7 +114,8 @@ See [Tailscale](/configuration/endpoint/tailscale/). Due to maintenance difficulties, sing-box 1.12.0 requires at least Go 1.23 to compile. -For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches from [MetaCubeX/go](https://github.com/MetaCubeX/go). +For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches +from [MetaCubeX/go](https://github.com/MetaCubeX/go). **7**: @@ -154,7 +177,8 @@ See [Tun](/configuration/inbound/tun/#loopback_address). We have significantly improved the performance of tun inbound on Apple platforms, especially in the gVisor stack. -The following data was tested using [tun_bench](https://github.com/SagerNet/sing-box/blob/dev-next/cmd/internal/tun_bench/main.go) on M4 MacBook pro. +The following data was tested +using [tun_bench](https://github.com/SagerNet/sing-box/blob/dev-next/cmd/internal/tun_bench/main.go) on M4 MacBook pro. | Version | Stack | MTU | Upload | Download | |-------------|--------|-------|--------|----------| @@ -173,8 +197,8 @@ The following data was tested using [tun_bench](https://github.com/SagerNet/sing **18**: -We continue to experience issues updating our sing-box apps on the App Store and Play Store. -Until we rewrite and resubmit the apps, they are considered irrecoverable. +We continue to experience issues updating our sing-box apps on the App Store and Play Store. +Until we rewrite and resubmit the apps, they are considered irrecoverable. Therefore, after this release, we will not be repeating this notice unless there is new information. ### 1.11.15 @@ -455,7 +479,8 @@ See [AnyTLS Inbound](/configuration/inbound/anytls/) and [AnyTLS Outbound](/conf **2**: -`resolve` route action now accepts `disable_cache` and other options like in DNS route actions, see [Route Action](/configuration/route/rule_action). +`resolve` route action now accepts `disable_cache` and other options like in DNS route actions, +see [Route Action](/configuration/route/rule_action). **3**: @@ -486,7 +511,8 @@ See [Tailscale](/configuration/endpoint/tailscale/). Due to maintenance difficulties, sing-box 1.12.0 requires at least Go 1.23 to compile. -For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches from [MetaCubeX/go](https://github.com/MetaCubeX/go). +For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches +from [MetaCubeX/go](https://github.com/MetaCubeX/go). ### 1.11.3 diff --git a/docs/sponsors.md b/docs/sponsors.md index d40d7709..33898928 100644 --- a/docs/sponsors.md +++ b/docs/sponsors.md @@ -11,16 +11,22 @@ the project maintainer via [GitHub Sponsors](https://github.com/sponsors/nekohas ![](https://nekohasekai.github.io/sponsor-images/sponsors.svg) -### Special Sponsors +## Commercial Sponsors -**Viral Tech, Inc.** +> [Warp](https://go.warp.dev/sing-box), Built for coding with multiple AI agents. + +[![](https://github.com/warpdotdev/brand-assets/raw/refs/heads/main/Github/Sponsor/Warp-Github-LG-02.png)](https://go.warp.dev/sing-box) + +## Special Sponsors + +> Viral Tech, Inc. Helping us re-list sing-box apps on the Apple Store. --- -[![JetBrains logo](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://www.jetbrains.com) +> [JetBrains](https://www.jetbrains.com) Free license for the amazing IDEs. ---- +[![JetBrains logo](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://www.jetbrains.com) diff --git a/experimental/libbox/service_pause.go b/experimental/libbox/service_pause.go index 791684ec..9c888454 100644 --- a/experimental/libbox/service_pause.go +++ b/experimental/libbox/service_pause.go @@ -12,9 +12,7 @@ type iOSPauseFields struct { func (s *BoxService) Pause() { s.pauseManager.DevicePause() - if !C.IsIos { - s.instance.Router().ResetNetwork() - } else { + if C.IsIos { if s.endPauseTimer == nil { s.endPauseTimer = time.AfterFunc(time.Minute, s.pauseManager.DeviceWake) } else { @@ -26,7 +24,6 @@ func (s *BoxService) Pause() { func (s *BoxService) Wake() { if !C.IsIos { s.pauseManager.DeviceWake() - s.instance.Router().ResetNetwork() } } diff --git a/go.mod b/go.mod index cf27f0a3..7c1341c7 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.24.4 toolchain go1.24.6 require ( - github.com/anytls/sing-anytls v0.0.8 + github.com/anytls/sing-anytls v0.0.11 github.com/caddyserver/certmagic v0.23.0 github.com/coder/websocket v1.8.13 github.com/cretz/bine v0.2.0 @@ -18,8 +18,8 @@ require ( github.com/libdns/alidns v1.0.5-libdns.v1.beta1 github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6 github.com/logrusorgru/aurora v2.0.3+incompatible - github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 - github.com/metacubex/utls v1.8.0 + github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0 + github.com/metacubex/utls v1.8.3 github.com/mholt/acmez/v3 v3.1.2 github.com/miekg/dns v1.1.67 github.com/oschwald/maxminddb-golang v1.13.1 @@ -30,17 +30,17 @@ require ( github.com/sagernet/fswatch v0.1.1 github.com/sagernet/gomobile v0.1.8 github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb - github.com/sagernet/quic-go v0.52.0-beta.1 - github.com/sagernet/sing v0.7.10 + github.com/sagernet/quic-go v0.52.0-sing-box-mod.3 + github.com/sagernet/sing v0.7.13 github.com/sagernet/sing-mux v0.3.3 github.com/sagernet/sing-quic v0.5.2-0.20250909083218-00a55617c0fb github.com/sagernet/sing-shadowsocks v0.2.8 github.com/sagernet/sing-shadowsocks2 v0.2.1 github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 - github.com/sagernet/sing-tun v0.7.2 + github.com/sagernet/sing-tun v0.7.3 github.com/sagernet/sing-vmess v0.2.7 github.com/sagernet/smux v1.5.34-mod.2 - github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.1 + github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.2 github.com/sagernet/wireguard-go v0.0.1-beta.7 github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 github.com/spf13/cobra v1.9.1 @@ -54,7 +54,6 @@ require ( golang.org/x/mod v0.27.0 golang.org/x/net v0.43.0 golang.org/x/sys v0.35.0 - golang.org/x/time v0.11.0 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 google.golang.org/grpc v1.73.0 google.golang.org/protobuf v1.36.6 @@ -143,6 +142,7 @@ require ( go.uber.org/zap/exp v0.3.0 // indirect go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect golang.org/x/term v0.34.0 // indirect + golang.org/x/time v0.11.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard/windows v0.5.3 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect diff --git a/go.sum b/go.sum index dd61d5b2..2b4280d7 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1O github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/anytls/sing-anytls v0.0.8 h1:1u/fnH1HoeeMV5mX7/eUOjLBvPdkd1UJRmXiRi6Vymc= -github.com/anytls/sing-anytls v0.0.8/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= +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/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU= @@ -128,10 +128,10 @@ github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= -github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 h1:j1VRTiC9JLR4nUbSikx9OGdu/3AgFDqgcLj4GoqyQkc= -github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw= -github.com/metacubex/utls v1.8.0 h1:mSYi6FMnmc5riARl5UZDmWVy710z+P5b7xuGW0lV9ac= -github.com/metacubex/utls v1.8.0/go.mod h1:FdjYzVfCtgtna19hX0ER1Xsa5uJInwdQ4IcaaI98lEQ= +github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0 h1:Ui+/2s5Qz0lSnDUBmEL12M5Oi/PzvFxGTNohm8ZcsmE= +github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw= +github.com/metacubex/utls v1.8.3 h1:0m/yCxm3SK6kWve2lKiFb1pue1wHitJ8sQQD4Ikqde4= +github.com/metacubex/utls v1.8.3/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko= github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc= github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0= @@ -172,11 +172,11 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= -github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs= -github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4= +github.com/sagernet/quic-go v0.52.0-sing-box-mod.3 h1:ySqffGm82rPqI1TUPqmtHIYd12pfEGScygnOxjTL56w= +github.com/sagernet/quic-go v0.52.0-sing-box-mod.3/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4= github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= -github.com/sagernet/sing v0.7.10 h1:2yPhZFx+EkyHPH8hXNezgyRSHyGY12CboId7CtwLROw= -github.com/sagernet/sing v0.7.10/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing v0.7.13 h1:XNYgd8e3cxMULs/LLJspdn/deHrnPWyrrglNHeCUAYM= +github.com/sagernet/sing v0.7.13/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing-mux v0.3.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw= github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA= github.com/sagernet/sing-quic v0.5.2-0.20250909083218-00a55617c0fb h1:5Wx3XeTiKrrrcrAky7Hc1bO3CGxrvho2Vu5b/adlEIM= @@ -187,14 +187,14 @@ github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnq github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w= github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA= -github.com/sagernet/sing-tun v0.7.2 h1:uJkAZM0KBqIYzrq077QGqdvj/+4i/pMOx6Pnx0jYqAs= -github.com/sagernet/sing-tun v0.7.2/go.mod h1:pUEjh9YHQ2gJT6Lk0TYDklh3WJy7lz+848vleGM3JPM= +github.com/sagernet/sing-tun v0.7.3 h1:MFnAir+l24ElEyxdfwtY8mqvUUL9nPnL9TDYLkOmVes= +github.com/sagernet/sing-tun v0.7.3/go.mod h1:pUEjh9YHQ2gJT6Lk0TYDklh3WJy7lz+848vleGM3JPM= github.com/sagernet/sing-vmess v0.2.7 h1:2ee+9kO0xW5P4mfe6TYVWf9VtY8k1JhNysBqsiYj0sk= github.com/sagernet/sing-vmess v0.2.7/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs= github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4= github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc= -github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.1 h1:gMC0q+0VvZBotZMZ9G0R8ZMEIT/Q6KnXbw0/OgMjmdk= -github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.1/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI= +github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.2 h1:MO7s4ni2bSfAOhcan2rdQSWCztkMXmqyg6jYPZp8bEE= +github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.2/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI= 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/shtorm-7/dnscrypt/v2 v2.4.0-extended-1.0.0 h1:e5s7RKBd2rIPR0StbvZ2vTVtJ5jDTsTk5wtIIapZTRg= diff --git a/protocol/anytls/outbound.go b/protocol/anytls/outbound.go index b026ed18..c8d8bf43 100644 --- a/protocol/anytls/outbound.go +++ b/protocol/anytls/outbound.go @@ -13,6 +13,7 @@ import ( "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" + E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/uot" @@ -43,6 +44,13 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL if options.TLS == nil || !options.TLS.Enabled { return nil, C.ErrTLSRequired } + // TCP Fast Open is incompatible with anytls because TFO creates a lazy connection + // that only establishes on first write. The lazy connection returns an empty address + // before establishment, but anytls SOCKS wrapper tries to access the remote address + // during handshake, causing a null pointer dereference crash. + if options.DialerOptions.TCPFastOpen { + return nil, E.New("tcp_fast_open is not supported with anytls outbound") + } tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS)) if err != nil { diff --git a/transport/v2raywebsocket/conn.go b/transport/v2raywebsocket/conn.go index 009cadd8..cb788fc9 100644 --- a/transport/v2raywebsocket/conn.go +++ b/transport/v2raywebsocket/conn.go @@ -291,7 +291,7 @@ func wrapWsError(err error) error { } var closedErr wsutil.ClosedError if errors.As(err, &closedErr) { - if closedErr.Code == ws.StatusNormalClosure { + if closedErr.Code == ws.StatusNormalClosure || closedErr.Code == ws.StatusNoStatusRcvd { err = io.EOF } }