mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-09-09 21:04:08 +08:00
Compare commits
24 Commits
4fb5ac292b
...
97f0dc8a60
Author | SHA1 | Date | |
---|---|---|---|
![]() |
97f0dc8a60 | ||
![]() |
ee02532ab5 | ||
![]() |
f1dd0dba78 | ||
![]() |
f4ed684146 | ||
![]() |
83f02d0bfb | ||
![]() |
52fa5f20a3 | ||
![]() |
f462ce5615 | ||
![]() |
cef3e538ba | ||
![]() |
acda4ce985 | ||
![]() |
354ece2bdf | ||
![]() |
de10bb00a9 | ||
![]() |
fdc181106d | ||
![]() |
8752b631bd | ||
![]() |
378e39f70c | ||
![]() |
043a2e7a07 | ||
![]() |
7e190e92ca | ||
![]() |
5eb318ba06 | ||
![]() |
4a209f1afb | ||
![]() |
c0ac3c748c | ||
![]() |
a65d3e040a | ||
![]() |
2358efe44a | ||
![]() |
09d3b8f2c2 | ||
![]() |
531de77124 | ||
![]() |
44981fd803 |
2
.github/setup_legacy_go.sh
vendored
2
.github/setup_legacy_go.sh
vendored
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
VERSION="1.23.6"
|
||||
VERSION="1.23.12"
|
||||
|
||||
mkdir -p $HOME/go
|
||||
cd $HOME/go
|
||||
|
56
.github/workflows/build.yml
vendored
56
.github/workflows/build.yml
vendored
@ -40,13 +40,13 @@ jobs:
|
||||
version: ${{ steps.outputs.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.24.6
|
||||
go-version: ^1.25.0
|
||||
- name: Check input version
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |-
|
||||
@ -88,13 +88,14 @@ jobs:
|
||||
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
|
||||
|
||||
- { os: windows, arch: amd64 }
|
||||
- { os: windows, arch: amd64, legacy_go: true }
|
||||
- { os: windows, arch: amd64, legacy_go123: true, legacy_name: "windows-7" }
|
||||
- { os: windows, arch: "386" }
|
||||
- { os: windows, arch: "386", legacy_go: true }
|
||||
- { os: windows, arch: "386", legacy_go123: true, legacy_name: "windows-7" }
|
||||
- { os: windows, arch: arm64 }
|
||||
|
||||
- { os: darwin, arch: amd64 }
|
||||
- { os: darwin, arch: arm64 }
|
||||
- { os: darwin, arch: amd64, legacy_go124: true, legacy_name: "macos-11" }
|
||||
|
||||
- { os: android, arch: arm64, ndk: "aarch64-linux-android21" }
|
||||
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" }
|
||||
@ -102,28 +103,33 @@ jobs:
|
||||
- { os: android, arch: "386", ndk: "i686-linux-android21" }
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
if: ${{ ! matrix.legacy_go }}
|
||||
if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }}
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.24.6
|
||||
- name: Cache Legacy Go
|
||||
if: matrix.require_legacy_go
|
||||
go-version: ^1.25.0
|
||||
- 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
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/go/go_legacy
|
||||
key: go_legacy_1236
|
||||
- name: Setup Legacy Go
|
||||
if: matrix.legacy_go && steps.cache-legacy-go.outputs.cache-hit != 'true'
|
||||
key: go_legacy_12312
|
||||
- name: Setup Go 1.23
|
||||
if: matrix.legacy_go123 && steps.cache-legacy-go.outputs.cache-hit != 'true'
|
||||
run: |-
|
||||
.github/setup_legacy_go.sh
|
||||
- name: Setup Legacy Go 2
|
||||
if: matrix.legacy_go
|
||||
- name: Setup Go 1.23
|
||||
if: matrix.legacy_go123
|
||||
run: |-
|
||||
echo "PATH=$HOME/go/go_legacy/bin:$PATH" >> $GITHUB_ENV
|
||||
echo "GOROOT=$HOME/go/go_legacy" >> $GITHUB_ENV
|
||||
@ -184,8 +190,8 @@ jobs:
|
||||
DIR_NAME="${DIR_NAME}-${{ matrix.go386 }}"
|
||||
elif [[ -n "${{ matrix.gomips }}" && "${{ matrix.gomips }}" != 'hardfloat' ]]; then
|
||||
DIR_NAME="${DIR_NAME}-${{ matrix.gomips }}"
|
||||
elif [[ "${{ matrix.legacy_go }}" == 'true' ]]; then
|
||||
DIR_NAME="${DIR_NAME}-legacy"
|
||||
elif [[ -n "${{ matrix.legacy_name }}" ]]; then
|
||||
DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
|
||||
fi
|
||||
echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
|
||||
PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
|
||||
@ -277,7 +283,7 @@ jobs:
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_go && '-legacy' || '' }}
|
||||
name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }}
|
||||
path: "dist"
|
||||
build_android:
|
||||
name: Build Android
|
||||
@ -287,14 +293,14 @@ jobs:
|
||||
- calculate_version
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: 'recursive'
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.24.6
|
||||
go-version: ^1.25.0
|
||||
- name: Setup Android NDK
|
||||
id: setup-ndk
|
||||
uses: nttld/setup-ndk@v1
|
||||
@ -367,14 +373,14 @@ jobs:
|
||||
- calculate_version
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: 'recursive'
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.24.6
|
||||
go-version: ^1.25.0
|
||||
- name: Setup Android NDK
|
||||
id: setup-ndk
|
||||
uses: nttld/setup-ndk@v1
|
||||
@ -464,7 +470,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
if: matrix.if
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: 'recursive'
|
||||
@ -472,7 +478,7 @@ jobs:
|
||||
if: matrix.if
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.24.6
|
||||
go-version: ^1.25.0
|
||||
- name: Setup Xcode stable
|
||||
if: matrix.if && github.ref == 'refs/heads/main-next'
|
||||
run: |-
|
||||
@ -624,7 +630,7 @@ jobs:
|
||||
- build_apple
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Cache ghr
|
||||
@ -647,7 +653,7 @@ jobs:
|
||||
git tag v${{ needs.calculate_version.outputs.version }} -f
|
||||
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
|
||||
- name: Download builds
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
4
.github/workflows/docker.yml
vendored
4
.github/workflows/docker.yml
vendored
@ -39,7 +39,7 @@ jobs:
|
||||
echo "ref=$ref"
|
||||
echo "ref=$ref" >> $GITHUB_OUTPUT
|
||||
- name: Checkout
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
ref: ${{ steps.ref.outputs.ref }}
|
||||
fetch-depth: 0
|
||||
@ -107,7 +107,7 @@ jobs:
|
||||
echo "latest=$latest"
|
||||
echo "latest=$latest" >> $GITHUB_OUTPUT
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
|
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
@ -22,15 +22,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.24.6
|
||||
go-version: ~1.24.6
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
uses: golangci/golangci-lint-action@v8
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout=30m
|
||||
|
21
.github/workflows/linux.yml
vendored
21
.github/workflows/linux.yml
vendored
@ -7,6 +7,11 @@ on:
|
||||
description: "Version name"
|
||||
required: true
|
||||
type: string
|
||||
forceBeta:
|
||||
description: "Force beta"
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
@ -19,13 +24,13 @@ jobs:
|
||||
version: ${{ steps.outputs.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.24.6
|
||||
go-version: ^1.25.0
|
||||
- name: Check input version
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |-
|
||||
@ -60,13 +65,13 @@ jobs:
|
||||
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64 }
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.24.6
|
||||
go-version: ^1.25.0
|
||||
- name: Setup Android NDK
|
||||
if: matrix.os == 'android'
|
||||
uses: nttld/setup-ndk@v1
|
||||
@ -99,11 +104,11 @@ jobs:
|
||||
run: |-
|
||||
TZ=UTC touch -t '197001010000' dist/sing-box
|
||||
- name: Set name
|
||||
if: ${{ ! contains(needs.calculate_version.outputs.version, '-') }}
|
||||
if: (! contains(needs.calculate_version.outputs.version, '-')) && !inputs.forceBeta
|
||||
run: |-
|
||||
echo "NAME=sing-box" >> "$GITHUB_ENV"
|
||||
- name: Set beta name
|
||||
if: contains(needs.calculate_version.outputs.version, '-')
|
||||
if: contains(needs.calculate_version.outputs.version, '-') || inputs.forceBeta
|
||||
run: |-
|
||||
echo "NAME=sing-box-beta" >> "$GITHUB_ENV"
|
||||
- name: Set version
|
||||
@ -166,7 +171,7 @@ jobs:
|
||||
- build
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set tag
|
||||
@ -175,7 +180,7 @@ jobs:
|
||||
git tag v${{ needs.calculate_version.outputs.version }} -f
|
||||
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
|
||||
- name: Download builds
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
@ -1,27 +1,6 @@
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- gofumpt
|
||||
- govet
|
||||
- gci
|
||||
- staticcheck
|
||||
- paralleltest
|
||||
- ineffassign
|
||||
|
||||
linters-settings:
|
||||
gci:
|
||||
custom-order: true
|
||||
sections:
|
||||
- standard
|
||||
- prefix(github.com/sagernet/)
|
||||
- default
|
||||
staticcheck:
|
||||
checks:
|
||||
- all
|
||||
- -SA1003
|
||||
|
||||
version: "2"
|
||||
run:
|
||||
go: "1.23"
|
||||
go: "1.24"
|
||||
build-tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
@ -30,7 +9,51 @@ run:
|
||||
- with_utls
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
|
||||
issues:
|
||||
exclude-dirs:
|
||||
- transport/simple-obfs
|
||||
linters:
|
||||
default: none
|
||||
enable:
|
||||
- govet
|
||||
- ineffassign
|
||||
- paralleltest
|
||||
- staticcheck
|
||||
settings:
|
||||
staticcheck:
|
||||
checks:
|
||||
- all
|
||||
- -S1000
|
||||
- -S1008
|
||||
- -S1017
|
||||
- -ST1003
|
||||
- -QF1001
|
||||
- -QF1003
|
||||
- -QF1008
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
paths:
|
||||
- transport/simple-obfs
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
enable:
|
||||
- gci
|
||||
- gofumpt
|
||||
settings:
|
||||
gci:
|
||||
sections:
|
||||
- standard
|
||||
- prefix(github.com/sagernet/)
|
||||
- default
|
||||
custom-order: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- transport/simple-obfs
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder
|
||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||
COPY . /go/src/github.com/sagernet/sing-box
|
||||
WORKDIR /go/src/github.com/sagernet/sing-box
|
||||
|
6
Makefile
6
Makefile
@ -45,7 +45,7 @@ lint:
|
||||
GOOS=freebsd golangci-lint run ./...
|
||||
|
||||
lint_install:
|
||||
go install -v github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
|
||||
|
||||
proto:
|
||||
@go run ./cmd/internal/protogen
|
||||
@ -245,8 +245,8 @@ lib:
|
||||
go run ./cmd/internal/build_libbox -target ios
|
||||
|
||||
lib_install:
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.7
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.7
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.8
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.8
|
||||
|
||||
docs:
|
||||
venv/bin/mkdocs serve
|
||||
|
@ -135,8 +135,7 @@ func ExtendContext(ctx context.Context) (context.Context, *InboundContext) {
|
||||
|
||||
func OverrideContext(ctx context.Context) context.Context {
|
||||
if metadata := ContextFrom(ctx); metadata != nil {
|
||||
var newMetadata InboundContext
|
||||
newMetadata = *metadata
|
||||
newMetadata := *metadata
|
||||
return WithContext(ctx, &newMetadata)
|
||||
}
|
||||
return ctx
|
||||
|
@ -20,6 +20,7 @@ type NetworkManager interface {
|
||||
DefaultOptions() NetworkOptions
|
||||
RegisterAutoRedirectOutputMark(mark uint32) error
|
||||
AutoRedirectOutputMark() uint32
|
||||
AutoRedirectOutputMarkFunc() control.Func
|
||||
NetworkMonitor() tun.NetworkUpdateMonitor
|
||||
InterfaceMonitor() tun.DefaultInterfaceMonitor
|
||||
PackageManager() tun.PackageManager
|
||||
|
@ -30,7 +30,7 @@ type Manager struct {
|
||||
outboundByTag map[string]adapter.Outbound
|
||||
dependByTag map[string][]string
|
||||
defaultOutbound adapter.Outbound
|
||||
defaultOutboundFallback adapter.Outbound
|
||||
defaultOutboundFallback func() (adapter.Outbound, error)
|
||||
}
|
||||
|
||||
func NewManager(logger logger.ContextLogger, registry adapter.OutboundRegistry, endpoint adapter.EndpointManager, defaultTag string) *Manager {
|
||||
@ -44,7 +44,7 @@ func NewManager(logger logger.ContextLogger, registry adapter.OutboundRegistry,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) Initialize(defaultOutboundFallback adapter.Outbound) {
|
||||
func (m *Manager) Initialize(defaultOutboundFallback func() (adapter.Outbound, error)) {
|
||||
m.defaultOutboundFallback = defaultOutboundFallback
|
||||
}
|
||||
|
||||
@ -55,18 +55,31 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
||||
}
|
||||
m.started = true
|
||||
m.stage = stage
|
||||
outbounds := m.outbounds
|
||||
m.access.Unlock()
|
||||
if stage == adapter.StartStateStart {
|
||||
if m.defaultOutbound == nil {
|
||||
directOutbound, err := m.defaultOutboundFallback()
|
||||
if err != nil {
|
||||
m.access.Unlock()
|
||||
return E.Cause(err, "create direct outbound for fallback")
|
||||
}
|
||||
m.outbounds = append(m.outbounds, directOutbound)
|
||||
m.outboundByTag[directOutbound.Tag()] = directOutbound
|
||||
m.defaultOutbound = directOutbound
|
||||
}
|
||||
if m.defaultTag != "" && m.defaultOutbound == nil {
|
||||
defaultEndpoint, loaded := m.endpoint.Get(m.defaultTag)
|
||||
if !loaded {
|
||||
m.access.Unlock()
|
||||
return E.New("default outbound not found: ", m.defaultTag)
|
||||
}
|
||||
m.defaultOutbound = defaultEndpoint
|
||||
}
|
||||
outbounds := m.outbounds
|
||||
m.access.Unlock()
|
||||
return m.startOutbounds(append(outbounds, common.Map(m.endpoint.Endpoints(), func(it adapter.Endpoint) adapter.Outbound { return it })...))
|
||||
} else {
|
||||
outbounds := m.outbounds
|
||||
m.access.Unlock()
|
||||
for _, outbound := range outbounds {
|
||||
err := adapter.LegacyStart(outbound, stage)
|
||||
if err != nil {
|
||||
@ -187,11 +200,7 @@ func (m *Manager) Outbound(tag string) (adapter.Outbound, bool) {
|
||||
func (m *Manager) Default() adapter.Outbound {
|
||||
m.access.RLock()
|
||||
defer m.access.RUnlock()
|
||||
if m.defaultOutbound != nil {
|
||||
return m.defaultOutbound
|
||||
} else {
|
||||
return m.defaultOutboundFallback
|
||||
}
|
||||
return m.defaultOutbound
|
||||
}
|
||||
|
||||
func (m *Manager) Remove(tag string) error {
|
||||
|
8
box.go
8
box.go
@ -314,15 +314,15 @@ func New(options Options) (*Box, error) {
|
||||
return nil, E.Cause(err, "initialize service[", i, "]")
|
||||
}
|
||||
}
|
||||
outboundManager.Initialize(common.Must1(
|
||||
direct.NewOutbound(
|
||||
outboundManager.Initialize(func() (adapter.Outbound, error) {
|
||||
return direct.NewOutbound(
|
||||
ctx,
|
||||
router,
|
||||
logFactory.NewLogger("outbound/direct"),
|
||||
"direct",
|
||||
option.DirectOutboundOptions{},
|
||||
),
|
||||
))
|
||||
)
|
||||
})
|
||||
dnsTransportManager.Initialize(common.Must1(
|
||||
local.NewTransport(
|
||||
ctx,
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 6db8e06e8d6c77648e79e3f93bdd41a4d48cc319
|
||||
Subproject commit 597c18482f6edc44e0c94b7cc849dd03b2121c45
|
@ -107,13 +107,13 @@ func buildAndroid() {
|
||||
}
|
||||
|
||||
if !debugEnabled {
|
||||
sharedFlags[3] = sharedFlags[3] + " -checklinkname=0"
|
||||
args = append(args, sharedFlags...)
|
||||
} else {
|
||||
debugFlags[1] = debugFlags[1] + " -checklinkname=0"
|
||||
args = append(args, debugFlags...)
|
||||
}
|
||||
|
||||
args = append(args, "-ldflags", "-checklinkname=0")
|
||||
|
||||
tags := append(sharedTags, memcTags...)
|
||||
if debugEnabled {
|
||||
tags = append(tags, debugTags...)
|
||||
|
@ -151,9 +151,7 @@ func testOnce(boxPath string, stackName string, mtu int, multiThread bool, flags
|
||||
var sudoArgs []string
|
||||
if len(flags) > 0 {
|
||||
sudoArgs = append(sudoArgs, "env")
|
||||
for _, flag := range flags {
|
||||
sudoArgs = append(sudoArgs, flag)
|
||||
}
|
||||
sudoArgs = append(sudoArgs, flags...)
|
||||
}
|
||||
sudoArgs = append(sudoArgs, boxPath, "run", "-c", tempConfig.Name())
|
||||
boxProcess := shell.Exec("sudo", sudoArgs...)
|
||||
|
@ -128,6 +128,10 @@ func (c *ReadWaitConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
func (c *ReadWaitConn) ReaderReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
var tlsRegistry []func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error)
|
||||
|
||||
func init() {
|
||||
|
@ -6,22 +6,26 @@ import (
|
||||
"net"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
|
||||
"github.com/metacubex/utls"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tlsRegistry = append(tlsRegistry, func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error) {
|
||||
tlsConn, loaded := common.Cast[*tls.UConn](conn)
|
||||
if !loaded {
|
||||
return
|
||||
switch tlsConn := conn.(type) {
|
||||
case *tls.UConn:
|
||||
return true, func() error {
|
||||
return utlsReadRecord(tlsConn.Conn)
|
||||
}, func() error {
|
||||
return utlsHandlePostHandshakeMessage(tlsConn.Conn)
|
||||
}
|
||||
case *tls.Conn:
|
||||
return true, func() error {
|
||||
return utlsReadRecord(tlsConn)
|
||||
}, func() error {
|
||||
return utlsHandlePostHandshakeMessage(tlsConn)
|
||||
}
|
||||
}
|
||||
return true, func() error {
|
||||
return utlsReadRecord(tlsConn.Conn)
|
||||
}, func() error {
|
||||
return utlsHandlePostHandshakeMessage(tlsConn.Conn)
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -121,11 +121,16 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
}
|
||||
}
|
||||
if options.RoutingMark == 0 && defaultOptions.RoutingMark != 0 {
|
||||
dialer.Control = control.Append(dialer.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
|
||||
listener.Control = control.Append(listener.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
|
||||
}
|
||||
}
|
||||
if options.RoutingMark == 0 && defaultOptions.RoutingMark != 0 {
|
||||
dialer.Control = control.Append(dialer.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
|
||||
listener.Control = control.Append(listener.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
|
||||
}
|
||||
}
|
||||
if networkManager != nil {
|
||||
markFunc := networkManager.AutoRedirectOutputMarkFunc()
|
||||
dialer.Control = control.Append(dialer.Control, markFunc)
|
||||
listener.Control = control.Append(listener.Control, markFunc)
|
||||
}
|
||||
if options.ReuseAddr {
|
||||
listener.Control = control.Append(listener.Control, control.ReuseAddr())
|
||||
@ -271,7 +276,7 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
|
||||
} else {
|
||||
dialer = d.udpDialer4
|
||||
}
|
||||
fastFallback := time.Now().Sub(d.networkLastFallback.Load()) < C.TCPTimeout
|
||||
fastFallback := time.Since(d.networkLastFallback.Load()) < C.TCPTimeout
|
||||
var (
|
||||
conn net.Conn
|
||||
isPrimary bool
|
||||
|
@ -10,6 +10,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@ -22,7 +24,7 @@ type slowOpenConn struct {
|
||||
ctx context.Context
|
||||
network string
|
||||
destination M.Socksaddr
|
||||
conn net.Conn
|
||||
conn atomic.Pointer[net.TCPConn]
|
||||
create chan struct{}
|
||||
done chan struct{}
|
||||
access sync.Mutex
|
||||
@ -50,22 +52,25 @@ func DialSlowContext(dialer *tcpDialer, ctx context.Context, network string, des
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) Read(b []byte) (n int, err error) {
|
||||
if c.conn == nil {
|
||||
select {
|
||||
case <-c.create:
|
||||
if c.err != nil {
|
||||
return 0, c.err
|
||||
}
|
||||
case <-c.done:
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
conn := c.conn.Load()
|
||||
if conn != nil {
|
||||
return conn.Read(b)
|
||||
}
|
||||
select {
|
||||
case <-c.create:
|
||||
if c.err != nil {
|
||||
return 0, c.err
|
||||
}
|
||||
return c.conn.Load().Read(b)
|
||||
case <-c.done:
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
return c.conn.Read(b)
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) Write(b []byte) (n int, err error) {
|
||||
if c.conn != nil {
|
||||
return c.conn.Write(b)
|
||||
tcpConn := c.conn.Load()
|
||||
if tcpConn != nil {
|
||||
return tcpConn.Write(b)
|
||||
}
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
@ -74,7 +79,7 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
|
||||
if c.err != nil {
|
||||
return 0, c.err
|
||||
}
|
||||
return c.conn.Write(b)
|
||||
return c.conn.Load().Write(b)
|
||||
case <-c.done:
|
||||
return 0, os.ErrClosed
|
||||
default:
|
||||
@ -83,7 +88,7 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
|
||||
if err != nil {
|
||||
c.err = err
|
||||
} else {
|
||||
c.conn = conn
|
||||
c.conn.Store(conn.(*net.TCPConn))
|
||||
}
|
||||
n = len(b)
|
||||
close(c.create)
|
||||
@ -93,70 +98,77 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
|
||||
func (c *slowOpenConn) Close() error {
|
||||
c.closeOnce.Do(func() {
|
||||
close(c.done)
|
||||
if c.conn != nil {
|
||||
c.conn.Close()
|
||||
conn := c.conn.Load()
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) LocalAddr() net.Addr {
|
||||
if c.conn == nil {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
return M.Socksaddr{}
|
||||
}
|
||||
return c.conn.LocalAddr()
|
||||
return conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) RemoteAddr() net.Addr {
|
||||
if c.conn == nil {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
return M.Socksaddr{}
|
||||
}
|
||||
return c.conn.RemoteAddr()
|
||||
return conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) SetDeadline(t time.Time) error {
|
||||
if c.conn == nil {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
return c.conn.SetDeadline(t)
|
||||
return conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) SetReadDeadline(t time.Time) error {
|
||||
if c.conn == nil {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
return c.conn.SetReadDeadline(t)
|
||||
return conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) SetWriteDeadline(t time.Time) error {
|
||||
if c.conn == nil {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
return conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) Upstream() any {
|
||||
return c.conn
|
||||
return common.PtrOrNil(c.conn.Load())
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) ReaderReplaceable() bool {
|
||||
return c.conn != nil
|
||||
return c.conn.Load() != nil
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) WriterReplaceable() bool {
|
||||
return c.conn != nil
|
||||
return c.conn.Load() != nil
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) LazyHeadroom() bool {
|
||||
return c.conn == nil
|
||||
return c.conn.Load() == nil
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) NeedHandshake() bool {
|
||||
return c.conn == nil
|
||||
return c.conn.Load() == nil
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) WriteTo(w io.Writer) (n int64, err error) {
|
||||
if c.conn == nil {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
select {
|
||||
case <-c.create:
|
||||
if c.err != nil {
|
||||
@ -166,5 +178,5 @@ func (c *slowOpenConn) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return 0, c.err
|
||||
}
|
||||
}
|
||||
return bufio.Copy(w, c.conn)
|
||||
return bufio.Copy(w, c.conn.Load())
|
||||
}
|
||||
|
@ -17,8 +17,5 @@ var uQUICChrome115 = &ja3.ClientHello{
|
||||
}
|
||||
|
||||
func maybeUQUIC(fingerprint *ja3.ClientHello) bool {
|
||||
if uQUICChrome115.Equals(fingerprint, true) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return !uQUICChrome115.Equals(fingerprint, true)
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ func (s *ECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (a
|
||||
func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
|
||||
s.access.Lock()
|
||||
defer s.access.Unlock()
|
||||
if len(s.ECHConfigList()) == 0 || s.lastTTL == 0 || time.Now().Sub(s.lastUpdate) > s.lastTTL {
|
||||
if len(s.ECHConfigList()) == 0 || s.lastTTL == 0 || time.Since(s.lastUpdate) > s.lastTTL {
|
||||
message := &mDNS.Msg{
|
||||
MsgHdr: mDNS.MsgHdr{
|
||||
RecursionDesired: true,
|
||||
|
@ -206,3 +206,11 @@ func (c *realityConnWrapper) Upstream() any {
|
||||
func (c *realityConnWrapper) CloseWrite() error {
|
||||
return c.Close()
|
||||
}
|
||||
|
||||
func (c *realityConnWrapper) ReaderReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *realityConnWrapper) WriterReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
@ -106,6 +106,14 @@ func (c *utlsConnWrapper) Upstream() any {
|
||||
return c.UConn
|
||||
}
|
||||
|
||||
func (c *utlsConnWrapper) ReaderReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *utlsConnWrapper) WriterReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type utlsALPNWrapper struct {
|
||||
utlsConnWrapper
|
||||
nextProtocols []string
|
||||
|
@ -109,6 +109,9 @@ func (c *Conn) Write(b []byte) (n int, err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if i != len(splitIndexes) {
|
||||
time.Sleep(c.fallbackDelay)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,10 @@ import (
|
||||
)
|
||||
|
||||
func writeAndWaitAck(ctx context.Context, conn *net.TCPConn, payload []byte, fallbackDelay time.Duration) error {
|
||||
_, err := conn.Write(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(fallbackDelay)
|
||||
return nil
|
||||
}
|
||||
|
@ -16,6 +16,9 @@ func writeAndWaitAck(ctx context.Context, conn *net.TCPConn, payload []byte, fal
|
||||
err := winiphlpapi.WriteAndWaitAck(ctx, conn, payload)
|
||||
if err != nil {
|
||||
if errors.Is(err, windows.ERROR_ACCESS_DENIED) {
|
||||
if _, err := conn.Write(payload); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(fallbackDelay)
|
||||
return nil
|
||||
}
|
||||
|
@ -293,12 +293,7 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg, options adapte
|
||||
} else if errors.Is(err, ErrResponseRejected) {
|
||||
rejected = true
|
||||
r.logger.DebugContext(ctx, E.Cause(err, "response rejected for ", FormatQuestion(message.Question[0].String())))
|
||||
/*} else if responseCheck!= nil && errors.Is(err, RcodeError(mDNS.RcodeNameError)) {
|
||||
rejected = true
|
||||
r.logger.DebugContext(ctx, E.Cause(err, "response rejected for ", FormatQuestion(message.Question[0].String())))
|
||||
*/
|
||||
} else if len(message.Question) > 0 {
|
||||
rejected = true
|
||||
r.logger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", FormatQuestion(message.Question[0].String())))
|
||||
} else {
|
||||
r.logger.ErrorContext(ctx, E.Cause(err, "exchange failed for <empty query>"))
|
||||
|
@ -104,7 +104,7 @@ func (c *dnsConfig) serverOffset() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (conf *dnsConfig) nameList(name string) []string {
|
||||
func (c *dnsConfig) nameList(name string) []string {
|
||||
l := len(name)
|
||||
rooted := l > 0 && name[l-1] == '.'
|
||||
if l > 254 || l == 254 && !rooted {
|
||||
@ -118,15 +118,15 @@ func (conf *dnsConfig) nameList(name string) []string {
|
||||
return []string{name}
|
||||
}
|
||||
|
||||
hasNdots := strings.Count(name, ".") >= conf.ndots
|
||||
hasNdots := strings.Count(name, ".") >= c.ndots
|
||||
name += "."
|
||||
// l++
|
||||
|
||||
names := make([]string, 0, 1+len(conf.search))
|
||||
names := make([]string, 0, 1+len(c.search))
|
||||
if hasNdots && !avoidDNS(name) {
|
||||
names = append(names, name)
|
||||
}
|
||||
for _, suffix := range conf.search {
|
||||
for _, suffix := range c.search {
|
||||
fqdn := name + suffix
|
||||
if !avoidDNS(fqdn) && len(fqdn) <= 254 {
|
||||
names = append(names, fqdn)
|
||||
|
@ -2,6 +2,10 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.12.2
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.12.1
|
||||
|
||||
* Fixes and improvements
|
||||
|
@ -61,7 +61,7 @@ WireGuard MTU。
|
||||
|
||||
==必填==
|
||||
|
||||
接口的 IPv4/IPv6 地址或地址段的列表您。
|
||||
接口的 IPv4/IPv6 地址或地址段的列表。
|
||||
|
||||
要分配给接口的 IP(v4 或 v6)地址段列表。
|
||||
|
||||
|
@ -88,7 +88,7 @@ icon: material/delete-clock
|
||||
|
||||
==必填==
|
||||
|
||||
接口的 IPv4/IPv6 地址或地址段的列表您。
|
||||
接口的 IPv4/IPv6 地址或地址段的列表。
|
||||
|
||||
要分配给接口的 IP(v4 或 v6)地址段列表。
|
||||
|
||||
|
@ -108,7 +108,7 @@ flowchart TB
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "tun",
|
||||
"inet4_address": "172.19.0.1/30",
|
||||
"address": ["172.19.0.1/30"],
|
||||
"auto_route": true,
|
||||
// "auto_redirect": true, // On linux
|
||||
"strict_route": true
|
||||
@ -162,8 +162,7 @@ flowchart TB
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "tun",
|
||||
"inet4_address": "172.19.0.1/30",
|
||||
"inet6_address": "fdfe:dcba:9876::1/126",
|
||||
"address": ["172.19.0.1/30", "fdfe:dcba:9876::1/126"],
|
||||
"auto_route": true,
|
||||
// "auto_redirect": true, // On linux
|
||||
"strict_route": true
|
||||
@ -233,8 +232,7 @@ flowchart TB
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "tun",
|
||||
"inet4_address": "172.19.0.1/30",
|
||||
"inet6_address": "fdfe:dcba:9876::1/126",
|
||||
"address": ["172.19.0.1/30","fdfe:dcba:9876::1/126"],
|
||||
"auto_route": true,
|
||||
// "auto_redirect": true, // On linux
|
||||
"strict_route": true
|
||||
|
@ -62,10 +62,7 @@ func (s *CommandServer) handleURLTest(conn net.Conn) error {
|
||||
return false
|
||||
}
|
||||
_, isGroup := it.(adapter.OutboundGroup)
|
||||
if isGroup {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return !isGroup
|
||||
})
|
||||
b, _ := batch.New(serviceNow.ctx, batch.WithConcurrencyNum[any](10))
|
||||
for _, detour := range outbounds {
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
"github.com/sagernet/sing/service"
|
||||
"github.com/sagernet/sing/service/filemanager"
|
||||
)
|
||||
|
||||
func BaseContext(platformInterface PlatformInterface) context.Context {
|
||||
@ -33,7 +34,9 @@ func BaseContext(platformInterface PlatformInterface) context.Context {
|
||||
})
|
||||
}
|
||||
}
|
||||
return box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), dnsRegistry, include.ServiceRegistry())
|
||||
ctx := context.Background()
|
||||
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
|
||||
return box.Context(ctx, include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), dnsRegistry, include.ServiceRegistry())
|
||||
}
|
||||
|
||||
func parseConfig(ctx context.Context, configContent string) (option.Options, error) {
|
||||
|
@ -18,6 +18,7 @@ func newIterator[T any](values []T) *iterator[T] {
|
||||
return &iterator[T]{values}
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func newPtrIterator[T any](values []T) *iterator[*T] {
|
||||
return &iterator[*T]{common.Map(values, func(value T) *T { return &value })}
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ import (
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service"
|
||||
"github.com/sagernet/sing/service/filemanager"
|
||||
"github.com/sagernet/sing/service/pause"
|
||||
)
|
||||
|
||||
@ -44,7 +43,6 @@ type BoxService struct {
|
||||
|
||||
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
||||
ctx := BaseContext(platformInterface)
|
||||
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
|
||||
service.MustRegister[deprecated.Manager](ctx, new(deprecatedManager))
|
||||
options, err := parseConfig(ctx, configContent)
|
||||
if err != nil {
|
||||
|
@ -115,7 +115,7 @@ func (s *StatsService) RoutedPacketConnection(ctx context.Context, conn N.Packet
|
||||
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
|
||||
}
|
||||
s.access.Unlock()
|
||||
return bufio.NewInt64CounterPacketConn(conn, readCounter, writeCounter)
|
||||
return bufio.NewInt64CounterPacketConn(conn, readCounter, nil, writeCounter, nil)
|
||||
}
|
||||
|
||||
func (s *StatsService) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
|
||||
@ -192,7 +192,7 @@ func (s *StatsService) GetSysStats(ctx context.Context, request *SysStatsRequest
|
||||
var rtm runtime.MemStats
|
||||
runtime.ReadMemStats(&rtm)
|
||||
response := &SysStatsResponse{
|
||||
Uptime: uint32(time.Now().Sub(s.createdAt).Seconds()),
|
||||
Uptime: uint32(time.Since(s.createdAt).Seconds()),
|
||||
NumGoroutine: uint32(runtime.NumGoroutine()),
|
||||
Alloc: rtm.Alloc,
|
||||
TotalAlloc: rtm.TotalAlloc,
|
||||
|
26
go.mod
26
go.mod
@ -15,7 +15,7 @@ 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-20241231083714-66613d49c422
|
||||
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4
|
||||
github.com/metacubex/utls v1.8.0
|
||||
github.com/mholt/acmez/v3 v3.1.2
|
||||
github.com/miekg/dns v1.1.67
|
||||
@ -24,17 +24,17 @@ require (
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||
github.com/sagernet/cors v1.2.1
|
||||
github.com/sagernet/fswatch v0.1.1
|
||||
github.com/sagernet/gomobile v0.1.7
|
||||
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.5
|
||||
github.com/sagernet/sing-mux v0.3.2
|
||||
github.com/sagernet/sing-quic v0.5.0-beta.3
|
||||
github.com/sagernet/sing v0.7.6-0.20250815070458-d33ece7a184f
|
||||
github.com/sagernet/sing-mux v0.3.3
|
||||
github.com/sagernet/sing-quic v0.5.0
|
||||
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.0-beta.1
|
||||
github.com/sagernet/sing-vmess v0.2.6
|
||||
github.com/sagernet/sing-vmess v0.2.7
|
||||
github.com/sagernet/smux v1.5.34-mod.2
|
||||
github.com/sagernet/tailscale v1.80.3-mod.5
|
||||
github.com/sagernet/wireguard-go v0.0.1-beta.7
|
||||
@ -44,11 +44,11 @@ require (
|
||||
github.com/vishvananda/netns v0.0.5
|
||||
go.uber.org/zap v1.27.0
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.40.0
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6
|
||||
golang.org/x/mod v0.26.0
|
||||
golang.org/x/net v0.42.0
|
||||
golang.org/x/sys v0.34.0
|
||||
golang.org/x/mod v0.27.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/sys v0.35.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
|
||||
@ -123,10 +123,10 @@ require (
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/term v0.33.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
golang.org/x/term v0.34.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
golang.org/x/tools v0.36.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
|
||||
|
52
go.sum
52
go.sum
@ -122,8 +122,8 @@ 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-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY=
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
|
||||
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/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
|
||||
@ -156,8 +156,8 @@ github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
|
||||
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
|
||||
github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs=
|
||||
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
||||
github.com/sagernet/gomobile v0.1.7 h1:I9jCJZTH0weP5MsuydvYHX5QfN/r6Fe8ptAIj1+SJVg=
|
||||
github.com/sagernet/gomobile v0.1.7/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
||||
github.com/sagernet/gomobile v0.1.8 h1:vXgoN0pjsMONAaYCTdsKBX2T1kxuS7sbT/mZ7PElGoo=
|
||||
github.com/sagernet/gomobile v0.1.8/go.mod h1:A8l3FlHi2D/+mfcd4HHvk5DGFPW/ShFb9jHP5VmSiDY=
|
||||
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb h1:pprQtDqNgqXkRsXn+0E8ikKOemzmum8bODjSfDene38=
|
||||
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb/go.mod h1:QkkPEJLw59/tfxgapHta14UL5qMUah5NXhO0Kw2Kan4=
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
||||
@ -167,12 +167,12 @@ github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/l
|
||||
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/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing v0.7.5 h1:gNMwZCLPqR+4e0g6dwi0sSsrvOmoMjpZgqxKsuJZatc=
|
||||
github.com/sagernet/sing v0.7.5/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
|
||||
github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
||||
github.com/sagernet/sing-quic v0.5.0-beta.3 h1:X/acRNsqQNfDlmwE7SorHfaZiny5e67hqIzM/592ric=
|
||||
github.com/sagernet/sing-quic v0.5.0-beta.3/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0=
|
||||
github.com/sagernet/sing v0.7.6-0.20250815070458-d33ece7a184f h1:HIBo8l+tsS3wLwuI1E56uRTQw46QytXSUpZTP3vwG/U=
|
||||
github.com/sagernet/sing v0.7.6-0.20250815070458-d33ece7a184f/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.0 h1:jNLIyVk24lFPvu8A4x+ZNEnZdI+Tg1rp7eCJ6v0Csak=
|
||||
github.com/sagernet/sing-quic v0.5.0/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
|
||||
@ -181,8 +181,8 @@ github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75
|
||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
|
||||
github.com/sagernet/sing-tun v0.7.0-beta.1 h1:mBIFXYAnGO5ey/HcCYanqnBx61E7yF8zTFGRZonGYmY=
|
||||
github.com/sagernet/sing-tun v0.7.0-beta.1/go.mod h1:AHJuRrLbNRJuivuFZ2VhXwDj4ViYp14szG5EkkKAqRQ=
|
||||
github.com/sagernet/sing-vmess v0.2.6 h1:1c4dGzeGy0kpBXXrT1sgiMZtHhdJylIT8eWrGhJYZec=
|
||||
github.com/sagernet/sing-vmess v0.2.6/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs=
|
||||
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-mod.5 h1:7V7z+p2C//TGtff20pPnDCt3qP6uFyY62peJoKF9z/A=
|
||||
@ -262,18 +262,18 @@ go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wus
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
|
||||
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
|
||||
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
||||
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
@ -285,20 +285,20 @@ golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
|
||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -100,7 +100,6 @@ func rewriteRcodeAction(rcodeMap map[string]int, ruleAction *DNSRuleAction) {
|
||||
}
|
||||
ruleAction.Action = C.RuleActionTypePredefined
|
||||
ruleAction.PredefinedOptions.Rcode = common.Ptr(DNSRCode(rcode))
|
||||
return
|
||||
}
|
||||
|
||||
type DNSClientOptions struct {
|
||||
|
@ -313,7 +313,7 @@ func (g *URLTestGroup) Select(network string) (adapter.Outbound, bool) {
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) loopCheck() {
|
||||
if time.Now().Sub(g.lastActive.Load()) > g.interval {
|
||||
if time.Since(g.lastActive.Load()) > g.interval {
|
||||
g.lastActive.Store(time.Now())
|
||||
g.CheckOutbounds(false)
|
||||
}
|
||||
@ -323,7 +323,7 @@ func (g *URLTestGroup) loopCheck() {
|
||||
return
|
||||
case <-g.ticker.C:
|
||||
}
|
||||
if time.Now().Sub(g.lastActive.Load()) > g.idleTimeout {
|
||||
if time.Since(g.lastActive.Load()) > g.idleTimeout {
|
||||
g.access.Lock()
|
||||
g.ticker.Stop()
|
||||
g.ticker = nil
|
||||
@ -360,7 +360,7 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint
|
||||
continue
|
||||
}
|
||||
history := g.history.LoadURLTestHistory(realTag)
|
||||
if !force && history != nil && time.Now().Sub(history.Time) < g.interval {
|
||||
if !force && history != nil && time.Since(history.Time) < g.interval {
|
||||
continue
|
||||
}
|
||||
checked[realTag] = true
|
||||
|
@ -128,7 +128,6 @@ func (h *Outbound) InterfaceUpdated() {
|
||||
if h.multiplexDialer != nil {
|
||||
h.multiplexDialer.Reset()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Outbound) Close() error {
|
||||
|
@ -180,7 +180,6 @@ func (s *Outbound) connect() (*ssh.Client, error) {
|
||||
|
||||
func (s *Outbound) InterfaceUpdated() {
|
||||
common.Close(s.clientConn)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Outbound) Close() error {
|
||||
|
@ -105,7 +105,6 @@ func (h *Outbound) InterfaceUpdated() {
|
||||
if h.multiplexDialer != nil {
|
||||
h.multiplexDialer.Reset()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Outbound) Close() error {
|
||||
|
@ -137,6 +137,9 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
if platformInterface != nil && platformInterface.UnderNetworkExtension() {
|
||||
// In Network Extension, when MTU exceeds 4064 (4096-UTUN_IF_HEADROOM_SIZE), the performance of tun will drop significantly, which may be a system bug.
|
||||
tunMTU = 4064
|
||||
} else if C.IsAndroid {
|
||||
// Some Android devices report ENOBUFS when using MTU 65535
|
||||
tunMTU = 9000
|
||||
} else {
|
||||
tunMTU = 65535
|
||||
}
|
||||
|
@ -124,7 +124,6 @@ func (h *Outbound) InterfaceUpdated() {
|
||||
if h.multiplexDialer != nil {
|
||||
h.multiplexDialer.Reset()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Outbound) Close() error {
|
||||
|
@ -108,7 +108,6 @@ func (h *Outbound) InterfaceUpdated() {
|
||||
if h.multiplexDialer != nil {
|
||||
h.multiplexDialer.Reset()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Outbound) Close() error {
|
||||
|
@ -312,7 +312,7 @@ func (r *NetworkManager) AutoDetectInterfaceFunc() control.Func {
|
||||
if r.interfaceMonitor == nil {
|
||||
return nil
|
||||
}
|
||||
bindFunc := control.BindToInterfaceFunc(r.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
|
||||
return control.BindToInterfaceFunc(r.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
|
||||
remoteAddr := M.ParseSocksaddr(address).Addr
|
||||
if remoteAddr.IsValid() {
|
||||
iif, err := r.interfaceFinder.ByAddr(remoteAddr)
|
||||
@ -326,16 +326,6 @@ func (r *NetworkManager) AutoDetectInterfaceFunc() control.Func {
|
||||
}
|
||||
return defaultInterface.Name, defaultInterface.Index, nil
|
||||
})
|
||||
return func(network, address string, conn syscall.RawConn) error {
|
||||
err := bindFunc(network, address, conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r.autoRedirectOutputMark > 0 {
|
||||
return control.RoutingMark(r.autoRedirectOutputMark)(network, address, conn)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -366,6 +356,15 @@ func (r *NetworkManager) AutoRedirectOutputMark() uint32 {
|
||||
return r.autoRedirectOutputMark
|
||||
}
|
||||
|
||||
func (r *NetworkManager) AutoRedirectOutputMarkFunc() control.Func {
|
||||
return func(network, address string, conn syscall.RawConn) error {
|
||||
if r.autoRedirectOutputMark == 0 {
|
||||
return nil
|
||||
}
|
||||
return control.RoutingMark(r.autoRedirectOutputMark)(network, address, conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *NetworkManager) NetworkMonitor() tun.NetworkUpdateMonitor {
|
||||
return r.networkMonitor
|
||||
}
|
||||
|
@ -182,9 +182,9 @@ func (t *resolve1Manager) logRequest(sender dbus.Sender, message ...any) context
|
||||
} else if metadata.ProcessInfo.UserId != 0 {
|
||||
prefix = F.ToString("uid:", metadata.ProcessInfo.UserId)
|
||||
}
|
||||
t.logger.InfoContext(ctx, "(", prefix, ") ", F.ToString(message...))
|
||||
t.logger.InfoContext(ctx, "(", prefix, ") ", strings.Join(F.MapToString(message), " "))
|
||||
} else {
|
||||
t.logger.InfoContext(ctx, F.ToString(message...))
|
||||
t.logger.InfoContext(ctx, strings.Join(F.MapToString(message), " "))
|
||||
}
|
||||
return adapter.WithContext(ctx, &metadata)
|
||||
}
|
||||
@ -280,7 +280,10 @@ func (t *resolve1Manager) ResolveAddress(sender dbus.Sender, ifIndex int32, fami
|
||||
},
|
||||
}
|
||||
ctx := t.logRequest(sender, "ResolveAddress ", link.iif.Name, familyToString(family), addr, flags)
|
||||
response, lookupErr := t.dnsRouter.Exchange(ctx, request, adapter.DNSQueryOptions{})
|
||||
var metadata adapter.InboundContext
|
||||
metadata.InboundType = t.Type()
|
||||
metadata.Inbound = t.Tag()
|
||||
response, lookupErr := t.dnsRouter.Exchange(adapter.WithContext(ctx, &metadata), request, adapter.DNSQueryOptions{})
|
||||
if lookupErr != nil {
|
||||
err = wrapError(err)
|
||||
return
|
||||
@ -301,7 +304,7 @@ func (t *resolve1Manager) ResolveAddress(sender dbus.Sender, ifIndex int32, fami
|
||||
return
|
||||
}
|
||||
|
||||
func (t *resolve1Manager) ResolveRecord(sender dbus.Sender, ifIndex int32, family int32, hostname string, qClass uint16, qType uint16, flags uint64) (records []ResourceRecord, outflags uint64, err *dbus.Error) {
|
||||
func (t *resolve1Manager) ResolveRecord(sender dbus.Sender, ifIndex int32, hostname string, qClass uint16, qType uint16, flags uint64) (records []ResourceRecord, outflags uint64, err *dbus.Error) {
|
||||
t.linkAccess.Lock()
|
||||
link, err := t.getLink(ifIndex)
|
||||
if err != nil {
|
||||
@ -320,8 +323,11 @@ func (t *resolve1Manager) ResolveRecord(sender dbus.Sender, ifIndex int32, famil
|
||||
},
|
||||
},
|
||||
}
|
||||
ctx := t.logRequest(sender, "ResolveRecord ", link.iif.Name, familyToString(family), hostname, mDNS.Class(qClass), mDNS.Type(qType), flags)
|
||||
response, exchangeErr := t.dnsRouter.Exchange(ctx, request, adapter.DNSQueryOptions{})
|
||||
ctx := t.logRequest(sender, "ResolveRecord", link.iif.Name, hostname, mDNS.Class(qClass), mDNS.Type(qType), flags)
|
||||
var metadata adapter.InboundContext
|
||||
metadata.InboundType = t.Type()
|
||||
metadata.Inbound = t.Tag()
|
||||
response, exchangeErr := t.dnsRouter.Exchange(adapter.WithContext(ctx, &metadata), request, adapter.DNSQueryOptions{})
|
||||
if exchangeErr != nil {
|
||||
err = wrapError(exchangeErr)
|
||||
return
|
||||
@ -341,6 +347,7 @@ func (t *resolve1Manager) ResolveRecord(sender dbus.Sender, ifIndex int32, famil
|
||||
err = wrapError(unpackErr)
|
||||
}
|
||||
record.Data = data
|
||||
records = append(records, record)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -380,8 +387,10 @@ func (t *resolve1Manager) ResolveService(sender dbus.Sender, ifIndex int32, host
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
srvResponse, exchangeErr := t.dnsRouter.Exchange(ctx, srvRequest, adapter.DNSQueryOptions{})
|
||||
var metadata adapter.InboundContext
|
||||
metadata.InboundType = t.Type()
|
||||
metadata.Inbound = t.Tag()
|
||||
srvResponse, exchangeErr := t.dnsRouter.Exchange(adapter.WithContext(ctx, &metadata), srvRequest, adapter.DNSQueryOptions{})
|
||||
if exchangeErr != nil {
|
||||
err = wrapError(exchangeErr)
|
||||
return
|
||||
|
@ -91,11 +91,6 @@ func (i *Service) Start(stage adapter.StartStage) error {
|
||||
return E.New("multiple resolved service are not supported")
|
||||
}
|
||||
}
|
||||
case adapter.StartStateStart:
|
||||
err := i.listener.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
systemBus, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -117,6 +112,11 @@ func (i *Service) Start(stage adapter.StartStage) error {
|
||||
return E.New("unknown request name reply: ", reply)
|
||||
}
|
||||
i.networkUpdateCallback = i.network.NetworkMonitor().RegisterCallback(i.onNetworkUpdate)
|
||||
case adapter.StartStateStart:
|
||||
err := i.listener.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -167,6 +167,8 @@ func (i *Service) exchangePacket0(ctx context.Context, buffer *buf.Buffer, oob [
|
||||
}
|
||||
var metadata adapter.InboundContext
|
||||
metadata.Source = source
|
||||
metadata.InboundType = i.Type()
|
||||
metadata.Inbound = i.Tag()
|
||||
response, err := i.dnsRouter.Exchange(adapter.WithContext(ctx, &metadata), &message, adapter.DNSQueryOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -156,18 +156,14 @@ func (s *APIServer) deleteUser(writer http.ResponseWriter, request *http.Request
|
||||
}
|
||||
|
||||
func (s *APIServer) getStats(writer http.ResponseWriter, request *http.Request) {
|
||||
requireClear := chi.URLParam(request, "clear") == "true"
|
||||
requireClear := request.URL.Query().Get("clear") == "true"
|
||||
|
||||
users := s.user.List()
|
||||
s.traffic.ReadUsers(users)
|
||||
s.traffic.ReadUsers(users, requireClear)
|
||||
for i := range users {
|
||||
users[i].Password = ""
|
||||
}
|
||||
uplinkBytes, downlinkBytes, uplinkPackets, downlinkPackets, tcpSessions, udpSessions := s.traffic.ReadGlobal()
|
||||
|
||||
if requireClear {
|
||||
s.traffic.Clear()
|
||||
}
|
||||
uplinkBytes, downlinkBytes, uplinkPackets, downlinkPackets, tcpSessions, udpSessions := s.traffic.ReadGlobal(requireClear)
|
||||
|
||||
render.JSON(writer, request, render.M{
|
||||
"uplinkBytes": uplinkBytes,
|
||||
|
@ -142,86 +142,82 @@ func (s *TrafficManager) TrackPacketConnection(conn N.PacketConn, metadata adapt
|
||||
readPacketCounter = append(readPacketCounter, upPacketsCounter)
|
||||
writePacketCounter = append(writePacketCounter, downPacketsCounter)
|
||||
udpSessionCounter.Add(1)
|
||||
return bufio.NewInt64CounterPacketConn(conn, append(readCounter, readPacketCounter...), append(writeCounter, writePacketCounter...))
|
||||
return bufio.NewInt64CounterPacketConn(conn, readCounter, readPacketCounter, writeCounter, writePacketCounter)
|
||||
}
|
||||
|
||||
func (s *TrafficManager) ReadUser(user *UserObject) {
|
||||
s.userAccess.Lock()
|
||||
defer s.userAccess.Unlock()
|
||||
s.readUser(user)
|
||||
s.readUser(user, false)
|
||||
}
|
||||
|
||||
func (s *TrafficManager) readUser(user *UserObject) {
|
||||
func (s *TrafficManager) readUser(user *UserObject, swap bool) {
|
||||
if counter, loaded := s.userUplink[user.UserName]; loaded {
|
||||
user.UplinkBytes = counter.Load()
|
||||
if swap {
|
||||
user.UplinkBytes = counter.Swap(0)
|
||||
} else {
|
||||
user.UplinkBytes = counter.Load()
|
||||
}
|
||||
}
|
||||
if counter, loaded := s.userDownlink[user.UserName]; loaded {
|
||||
user.DownlinkBytes = counter.Load()
|
||||
if swap {
|
||||
user.DownlinkBytes = counter.Swap(0)
|
||||
} else {
|
||||
user.DownlinkBytes = counter.Load()
|
||||
}
|
||||
}
|
||||
if counter, loaded := s.userUplinkPackets[user.UserName]; loaded {
|
||||
user.UplinkPackets = counter.Load()
|
||||
if swap {
|
||||
user.UplinkPackets = counter.Swap(0)
|
||||
} else {
|
||||
user.UplinkPackets = counter.Load()
|
||||
}
|
||||
}
|
||||
if counter, loaded := s.userDownlinkPackets[user.UserName]; loaded {
|
||||
user.DownlinkPackets = counter.Load()
|
||||
if swap {
|
||||
user.DownlinkPackets = counter.Swap(0)
|
||||
} else {
|
||||
user.DownlinkPackets = counter.Load()
|
||||
}
|
||||
}
|
||||
if counter, loaded := s.userTCPSessions[user.UserName]; loaded {
|
||||
user.TCPSessions = counter.Load()
|
||||
if swap {
|
||||
user.TCPSessions = counter.Swap(0)
|
||||
} else {
|
||||
user.TCPSessions = counter.Load()
|
||||
}
|
||||
}
|
||||
if counter, loaded := s.userUDPSessions[user.UserName]; loaded {
|
||||
user.UDPSessions = counter.Load()
|
||||
if swap {
|
||||
user.UDPSessions = counter.Swap(0)
|
||||
} else {
|
||||
user.UDPSessions = counter.Load()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TrafficManager) ReadUsers(users []*UserObject) {
|
||||
func (s *TrafficManager) ReadUsers(users []*UserObject, swap bool) {
|
||||
s.userAccess.Lock()
|
||||
defer s.userAccess.Unlock()
|
||||
for _, user := range users {
|
||||
s.readUser(user)
|
||||
s.readUser(user, swap)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *TrafficManager) ReadGlobal() (
|
||||
uplinkBytes int64,
|
||||
downlinkBytes int64,
|
||||
uplinkPackets int64,
|
||||
downlinkPackets int64,
|
||||
tcpSessions int64,
|
||||
udpSessions int64,
|
||||
) {
|
||||
return s.globalUplink.Load(),
|
||||
s.globalDownlink.Load(),
|
||||
s.globalUplinkPackets.Load(),
|
||||
s.globalDownlinkPackets.Load(),
|
||||
s.globalTCPSessions.Load(),
|
||||
s.globalUDPSessions.Load()
|
||||
}
|
||||
|
||||
func (s *TrafficManager) Clear() {
|
||||
s.globalUplink.Store(0)
|
||||
s.globalDownlink.Store(0)
|
||||
s.globalUplinkPackets.Store(0)
|
||||
s.globalDownlinkPackets.Store(0)
|
||||
s.globalTCPSessions.Store(0)
|
||||
s.globalUDPSessions.Store(0)
|
||||
s.userAccess.Lock()
|
||||
defer s.userAccess.Unlock()
|
||||
for _, counter := range s.userUplink {
|
||||
counter.Store(0)
|
||||
}
|
||||
for _, counter := range s.userDownlink {
|
||||
counter.Store(0)
|
||||
}
|
||||
for _, counter := range s.userUplinkPackets {
|
||||
counter.Store(0)
|
||||
}
|
||||
for _, counter := range s.userDownlinkPackets {
|
||||
counter.Store(0)
|
||||
}
|
||||
for _, counter := range s.userTCPSessions {
|
||||
counter.Store(0)
|
||||
}
|
||||
for _, counter := range s.userUDPSessions {
|
||||
counter.Store(0)
|
||||
func (s *TrafficManager) ReadGlobal(swap bool) (uplinkBytes int64, downlinkBytes int64, uplinkPackets int64, downlinkPackets int64, tcpSessions int64, udpSessions int64) {
|
||||
if swap {
|
||||
return s.globalUplink.Swap(0),
|
||||
s.globalDownlink.Swap(0),
|
||||
s.globalUplinkPackets.Swap(0),
|
||||
s.globalDownlinkPackets.Swap(0),
|
||||
s.globalTCPSessions.Swap(0),
|
||||
s.globalUDPSessions.Swap(0)
|
||||
} else {
|
||||
return s.globalUplink.Load(),
|
||||
s.globalDownlink.Load(),
|
||||
s.globalUplinkPackets.Load(),
|
||||
s.globalDownlinkPackets.Load(),
|
||||
s.globalTCPSessions.Load(),
|
||||
s.globalUDPSessions.Load()
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@ -29,7 +30,7 @@ type Client struct {
|
||||
serverAddr string
|
||||
serviceName string
|
||||
dialOptions []grpc.DialOption
|
||||
conn *grpc.ClientConn
|
||||
conn atomic.Pointer[grpc.ClientConn]
|
||||
connAccess sync.Mutex
|
||||
}
|
||||
|
||||
@ -74,13 +75,13 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
||||
}
|
||||
|
||||
func (c *Client) connect() (*grpc.ClientConn, error) {
|
||||
conn := c.conn
|
||||
conn := c.conn.Load()
|
||||
if conn != nil && conn.GetState() != connectivity.Shutdown {
|
||||
return conn, nil
|
||||
}
|
||||
c.connAccess.Lock()
|
||||
defer c.connAccess.Unlock()
|
||||
conn = c.conn
|
||||
conn = c.conn.Load()
|
||||
if conn != nil && conn.GetState() != connectivity.Shutdown {
|
||||
return conn, nil
|
||||
}
|
||||
@ -89,7 +90,7 @@ func (c *Client) connect() (*grpc.ClientConn, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.conn = conn
|
||||
c.conn.Store(conn)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
@ -109,11 +110,9 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
c.connAccess.Lock()
|
||||
defer c.connAccess.Unlock()
|
||||
if c.conn != nil {
|
||||
c.conn.Close()
|
||||
c.conn = nil
|
||||
conn := c.conn.Swap(nil)
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-quic"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@ -29,7 +30,7 @@ type Client struct {
|
||||
tlsConfig tls.Config
|
||||
quicConfig *quic.Config
|
||||
connAccess sync.Mutex
|
||||
conn quic.Connection
|
||||
conn atomic.TypedValue[quic.Connection]
|
||||
rawConn net.Conn
|
||||
}
|
||||
|
||||
@ -50,13 +51,13 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
||||
}
|
||||
|
||||
func (c *Client) offer() (quic.Connection, error) {
|
||||
conn := c.conn
|
||||
conn := c.conn.Load()
|
||||
if conn != nil && !common.Done(conn.Context()) {
|
||||
return conn, nil
|
||||
}
|
||||
c.connAccess.Lock()
|
||||
defer c.connAccess.Unlock()
|
||||
conn = c.conn
|
||||
conn = c.conn.Load()
|
||||
if conn != nil && !common.Done(conn.Context()) {
|
||||
return conn, nil
|
||||
}
|
||||
@ -72,14 +73,13 @@ func (c *Client) offerNew() (quic.Connection, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var packetConn net.PacketConn
|
||||
packetConn = bufio.NewUnbindPacketConn(udpConn)
|
||||
packetConn := bufio.NewUnbindPacketConn(udpConn)
|
||||
quicConn, err := qtls.Dial(c.ctx, packetConn, udpConn.RemoteAddr(), c.tlsConfig, c.quicConfig)
|
||||
if err != nil {
|
||||
packetConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
c.conn = quicConn
|
||||
c.conn.Store(quicConn)
|
||||
c.rawConn = udpConn
|
||||
return quicConn, nil
|
||||
}
|
||||
@ -99,13 +99,13 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
func (c *Client) Close() error {
|
||||
c.connAccess.Lock()
|
||||
defer c.connAccess.Unlock()
|
||||
if c.conn != nil {
|
||||
c.conn.CloseWithError(0, "")
|
||||
conn := c.conn.Swap(nil)
|
||||
if conn != nil {
|
||||
conn.CloseWithError(0, "")
|
||||
}
|
||||
if c.rawConn != nil {
|
||||
c.rawConn.Close()
|
||||
}
|
||||
c.conn = nil
|
||||
c.rawConn = nil
|
||||
return nil
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
@ -135,20 +136,22 @@ func (c *WebsocketConn) Upstream() any {
|
||||
type EarlyWebsocketConn struct {
|
||||
*Client
|
||||
ctx context.Context
|
||||
conn *WebsocketConn
|
||||
conn atomic.Pointer[WebsocketConn]
|
||||
access sync.Mutex
|
||||
create chan struct{}
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *EarlyWebsocketConn) Read(b []byte) (n int, err error) {
|
||||
if c.conn == nil {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
<-c.create
|
||||
if c.err != nil {
|
||||
return 0, c.err
|
||||
}
|
||||
conn = c.conn.Load()
|
||||
}
|
||||
return wrapWsError0(c.conn.Read(b))
|
||||
return wrapWsError0(conn.Read(b))
|
||||
}
|
||||
|
||||
func (c *EarlyWebsocketConn) writeRequest(content []byte) error {
|
||||
@ -187,21 +190,23 @@ func (c *EarlyWebsocketConn) writeRequest(content []byte) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.conn = conn
|
||||
c.conn.Store(conn)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) {
|
||||
if c.conn != nil {
|
||||
return wrapWsError0(c.conn.Write(b))
|
||||
conn := c.conn.Load()
|
||||
if conn != nil {
|
||||
return wrapWsError0(conn.Write(b))
|
||||
}
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
conn = c.conn.Load()
|
||||
if c.err != nil {
|
||||
return 0, c.err
|
||||
}
|
||||
if c.conn != nil {
|
||||
return wrapWsError0(c.conn.Write(b))
|
||||
if conn != nil {
|
||||
return wrapWsError0(conn.Write(b))
|
||||
}
|
||||
err = c.writeRequest(b)
|
||||
c.err = err
|
||||
@ -213,17 +218,19 @@ func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) {
|
||||
}
|
||||
|
||||
func (c *EarlyWebsocketConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
if c.conn != nil {
|
||||
return wrapWsError(c.conn.WriteBuffer(buffer))
|
||||
conn := c.conn.Load()
|
||||
if conn != nil {
|
||||
return wrapWsError(conn.WriteBuffer(buffer))
|
||||
}
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
if c.conn != nil {
|
||||
return wrapWsError(c.conn.WriteBuffer(buffer))
|
||||
}
|
||||
if c.err != nil {
|
||||
return c.err
|
||||
}
|
||||
conn = c.conn.Load()
|
||||
if conn != nil {
|
||||
return wrapWsError(conn.WriteBuffer(buffer))
|
||||
}
|
||||
err := c.writeRequest(buffer.Bytes())
|
||||
c.err = err
|
||||
close(c.create)
|
||||
@ -231,24 +238,27 @@ func (c *EarlyWebsocketConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
}
|
||||
|
||||
func (c *EarlyWebsocketConn) Close() error {
|
||||
if c.conn == nil {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
return nil
|
||||
}
|
||||
return c.conn.Close()
|
||||
return conn.Close()
|
||||
}
|
||||
|
||||
func (c *EarlyWebsocketConn) LocalAddr() net.Addr {
|
||||
if c.conn == nil {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
return M.Socksaddr{}
|
||||
}
|
||||
return c.conn.LocalAddr()
|
||||
return conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *EarlyWebsocketConn) RemoteAddr() net.Addr {
|
||||
if c.conn == nil {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
return M.Socksaddr{}
|
||||
}
|
||||
return c.conn.RemoteAddr()
|
||||
return conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (c *EarlyWebsocketConn) SetDeadline(t time.Time) error {
|
||||
@ -268,11 +278,11 @@ func (c *EarlyWebsocketConn) NeedAdditionalReadDeadline() bool {
|
||||
}
|
||||
|
||||
func (c *EarlyWebsocketConn) Upstream() any {
|
||||
return common.PtrOrNil(c.conn)
|
||||
return common.PtrOrNil(c.conn.Load())
|
||||
}
|
||||
|
||||
func (c *EarlyWebsocketConn) LazyHeadroom() bool {
|
||||
return c.conn == nil
|
||||
return c.conn.Load() == nil
|
||||
}
|
||||
|
||||
func wrapWsError(err error) error {
|
||||
|
Loading…
x
Reference in New Issue
Block a user