Compare commits
55 Commits
v1.11.0-al
...
dev-go124-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
658d4a180b | ||
|
|
93966bdd36 | ||
|
|
14a4b0f922 | ||
|
|
919b08e64c | ||
|
|
bdd2472065 | ||
|
|
705c23866a | ||
|
|
ec310170cc | ||
|
|
7405b22dc8 | ||
|
|
cd2cf2450e | ||
|
|
a525f139dc | ||
|
|
cc8ba050dd | ||
|
|
987556fd3d | ||
|
|
e6dd7d279d | ||
|
|
b1cbf141c7 | ||
|
|
ab616d8510 | ||
|
|
097818dfef | ||
|
|
47af425e45 | ||
|
|
5d49685683 | ||
|
|
15624a648c | ||
|
|
1bfedf5332 | ||
|
|
10d15259b6 | ||
|
|
ee11ca4935 | ||
|
|
e83331c2d9 | ||
|
|
b74df53a9c | ||
|
|
f64107f040 | ||
|
|
7db0e712b6 | ||
|
|
edfdf1d4f3 | ||
|
|
eb3023d66c | ||
|
|
2c39c4d19c | ||
|
|
aa3fcefb72 | ||
|
|
ce503fd682 | ||
|
|
017fa5e298 | ||
|
|
94d187397d | ||
|
|
1d497b08d7 | ||
|
|
e52ab781bb | ||
|
|
2c1e398c78 | ||
|
|
d4ad7ff638 | ||
|
|
ee0f3ba739 | ||
|
|
b365b1369d | ||
|
|
69f8d7fa72 | ||
|
|
746f63a9ac | ||
|
|
f0b6818b4c | ||
|
|
3032317918 | ||
|
|
db22f61846 | ||
|
|
8c3a98faa2 | ||
|
|
1e787cb607 | ||
|
|
558585b01d | ||
|
|
6e7ecbd4f5 | ||
|
|
5a661cde67 | ||
|
|
3cc0e87cfb | ||
|
|
effea5a2b3 | ||
|
|
7f168c5ec6 | ||
|
|
0e9129ee3f | ||
|
|
1086d5e665 | ||
|
|
d9102ba599 |
615
.github/workflows/build.yml
vendored
Normal file
615
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,615 @@
|
|||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: "Version name"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
prerelease:
|
||||||
|
description: "Is prerelease"
|
||||||
|
required: true
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
build:
|
||||||
|
description: "Build type"
|
||||||
|
required: true
|
||||||
|
type: choice
|
||||||
|
default: "All"
|
||||||
|
options:
|
||||||
|
- All
|
||||||
|
- Binary
|
||||||
|
- Android
|
||||||
|
- Apple
|
||||||
|
- app-store
|
||||||
|
- iOS
|
||||||
|
- macOS
|
||||||
|
- tvOS
|
||||||
|
- macOS-standalone
|
||||||
|
- publish-android
|
||||||
|
macos_project_version:
|
||||||
|
description: "macOS project version"
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main-next
|
||||||
|
- dev-next
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}-${{ inputs.build }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
calculate_version:
|
||||||
|
name: Calculate version
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.outputs.outputs.version }}
|
||||||
|
prerelease: ${{ steps.outputs.outputs.prerelease }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ^1.23
|
||||||
|
- name: Check input version
|
||||||
|
if: github.event_name == 'workflow_dispatch'
|
||||||
|
run: |-
|
||||||
|
echo "version=${{ inputs.version }}"
|
||||||
|
echo "prerelease=${{ inputs.prerelease }}"
|
||||||
|
echo "version=${{ inputs.version }}" >> "$GITHUB_ENV"
|
||||||
|
echo "prerelease=${{ inputs.prerelease }}" >> "$GITHUB_ENV"
|
||||||
|
- name: Calculate version
|
||||||
|
if: github.event_name != 'workflow_dispatch'
|
||||||
|
run: |-
|
||||||
|
go run -v ./cmd/internal/read_tag --nightly
|
||||||
|
- name: Set outputs
|
||||||
|
id: outputs
|
||||||
|
run: |-
|
||||||
|
echo "version=$version" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "prerelease=$prerelease" >> "$GITHUB_OUTPUT"
|
||||||
|
build:
|
||||||
|
name: Build binary
|
||||||
|
if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- calculate_version
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- name: linux_386
|
||||||
|
goos: linux
|
||||||
|
goarch: 386
|
||||||
|
- name: linux_amd64
|
||||||
|
goos: linux
|
||||||
|
goarch: amd64
|
||||||
|
- name: linux_arm64
|
||||||
|
goos: linux
|
||||||
|
goarch: arm64
|
||||||
|
- name: linux_arm
|
||||||
|
goos: linux
|
||||||
|
goarch: arm
|
||||||
|
goarm: 6
|
||||||
|
- name: linux_arm_v7
|
||||||
|
goos: linux
|
||||||
|
goarch: arm
|
||||||
|
goarm: 7
|
||||||
|
- name: linux_s390x
|
||||||
|
goos: linux
|
||||||
|
goarch: s390x
|
||||||
|
- name: linux_riscv64
|
||||||
|
goos: linux
|
||||||
|
goarch: riscv64
|
||||||
|
- name: linux_mips64le
|
||||||
|
goos: linux
|
||||||
|
goarch: mips64le
|
||||||
|
- name: windows_amd64
|
||||||
|
goos: windows
|
||||||
|
goarch: amd64
|
||||||
|
require_legacy_go: true
|
||||||
|
- name: windows_386
|
||||||
|
goos: windows
|
||||||
|
goarch: 386
|
||||||
|
require_legacy_go: true
|
||||||
|
- name: windows_arm64
|
||||||
|
goos: windows
|
||||||
|
goarch: arm64
|
||||||
|
- name: darwin_arm64
|
||||||
|
goos: darwin
|
||||||
|
goarch: arm64
|
||||||
|
- name: darwin_amd64
|
||||||
|
goos: darwin
|
||||||
|
goarch: amd64
|
||||||
|
require_legacy_go: true
|
||||||
|
- name: android_arm64
|
||||||
|
goos: android
|
||||||
|
goarch: arm64
|
||||||
|
- name: android_arm
|
||||||
|
goos: android
|
||||||
|
goarch: arm
|
||||||
|
goarm: 7
|
||||||
|
- name: android_amd64
|
||||||
|
goos: android
|
||||||
|
goarch: amd64
|
||||||
|
- name: android_386
|
||||||
|
goos: android
|
||||||
|
goarch: 386
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ^1.23
|
||||||
|
- name: Cache legacy Go
|
||||||
|
if: matrix.require_legacy_go
|
||||||
|
id: cache-legacy-go
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/go/go1.20.14
|
||||||
|
key: go120
|
||||||
|
- name: Setup legacy Go
|
||||||
|
if: matrix.require_legacy_go == 'true' && steps.cache-legacy-go.outputs.cache-hit != 'true'
|
||||||
|
run: |-
|
||||||
|
wget https://dl.google.com/go/go1.20.14.linux-amd64.tar.gz
|
||||||
|
tar -xzf go1.20.14.linux-amd64.tar.gz
|
||||||
|
mv go $HOME/go/go1.20.14
|
||||||
|
- name: Setup Android NDK
|
||||||
|
if: matrix.goos == 'android'
|
||||||
|
uses: nttld/setup-ndk@v1
|
||||||
|
with:
|
||||||
|
ndk-version: r28-beta2
|
||||||
|
local-cache: true
|
||||||
|
- name: Setup Goreleaser
|
||||||
|
uses: goreleaser/goreleaser-action@v6
|
||||||
|
with:
|
||||||
|
distribution: goreleaser-pro
|
||||||
|
version: latest
|
||||||
|
install-only: true
|
||||||
|
- name: Extract signing key
|
||||||
|
run: |-
|
||||||
|
mkdir -p $HOME/.gnupg
|
||||||
|
cat > $HOME/.gnupg/sagernet.key <<EOF
|
||||||
|
${{ secrets.GPG_KEY }}
|
||||||
|
EOF
|
||||||
|
echo "HOME=$HOME" >> "$GITHUB_ENV"
|
||||||
|
- name: Set tag
|
||||||
|
run: |-
|
||||||
|
git tag v${{ needs.calculate_version.outputs.version }}
|
||||||
|
- name: Build
|
||||||
|
if: matrix.goos != 'android'
|
||||||
|
run: |-
|
||||||
|
goreleaser release --clean --split
|
||||||
|
env:
|
||||||
|
GOOS: ${{ matrix.goos }}
|
||||||
|
GOARCH: ${{ matrix.goarch }}
|
||||||
|
GOPATH: ${{ env.HOME }}/go
|
||||||
|
GOARM: ${{ matrix.goarm }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||||
|
NFPM_KEY_PATH: ${{ env.HOME }}/.gnupg/sagernet.key
|
||||||
|
NFPM_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||||
|
- name: Build Android
|
||||||
|
if: matrix.goos == 'android'
|
||||||
|
run: |-
|
||||||
|
go install -v ./cmd/internal/build
|
||||||
|
GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build goreleaser release --clean --split
|
||||||
|
env:
|
||||||
|
BUILD_GOOS: ${{ matrix.goos }}
|
||||||
|
BUILD_GOARCH: ${{ matrix.goarch }}
|
||||||
|
GOARM: ${{ matrix.goarm }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||||
|
NFPM_KEY_PATH: ${{ env.HOME }}/.gnupg/sagernet.key
|
||||||
|
NFPM_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||||
|
- name: Upload artifact
|
||||||
|
if: github.event_name == 'workflow_dispatch'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: binary-${{ matrix.name }}
|
||||||
|
path: 'dist'
|
||||||
|
build_android:
|
||||||
|
name: Build Android
|
||||||
|
if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Android'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- calculate_version
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ^1.23
|
||||||
|
- name: Setup Android NDK
|
||||||
|
id: setup-ndk
|
||||||
|
uses: nttld/setup-ndk@v1
|
||||||
|
with:
|
||||||
|
ndk-version: r28-beta2
|
||||||
|
- name: Setup OpenJDK
|
||||||
|
run: |-
|
||||||
|
sudo apt update && sudo apt install -y openjdk-17-jdk-headless
|
||||||
|
/usr/lib/jvm/java-17-openjdk-amd64/bin/java --version
|
||||||
|
- name: Set tag
|
||||||
|
run: |-
|
||||||
|
git tag v${{ needs.calculate_version.outputs.version }}
|
||||||
|
- name: Build library
|
||||||
|
run: |-
|
||||||
|
make lib_install
|
||||||
|
export PATH="$PATH:$(go env GOPATH)/bin"
|
||||||
|
make lib_android
|
||||||
|
env:
|
||||||
|
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
|
||||||
|
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||||
|
- name: Checkout main branch
|
||||||
|
if: needs.calculate_version.outputs.prerelease == 'false'
|
||||||
|
run: |-
|
||||||
|
cd clients/android
|
||||||
|
git checkout main
|
||||||
|
- name: Checkout dev branch
|
||||||
|
if: needs.calculate_version.outputs.prerelease == 'true'
|
||||||
|
run: |-
|
||||||
|
cd clients/android
|
||||||
|
git checkout dev
|
||||||
|
- name: Gradle cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.gradle
|
||||||
|
key: gradle-${{ hashFiles('**/*.gradle') }}
|
||||||
|
- name: Build
|
||||||
|
run: |-
|
||||||
|
go run -v ./cmd/internal/update_android_version --ci
|
||||||
|
mkdir clients/android/app/libs
|
||||||
|
cp libbox.aar clients/android/app/libs
|
||||||
|
cd clients/android
|
||||||
|
./gradlew :app:assemblePlayRelease :app:assembleOtherRelease
|
||||||
|
env:
|
||||||
|
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
|
||||||
|
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||||
|
LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }}
|
||||||
|
- name: Prepare upload
|
||||||
|
if: github.event_name == 'workflow_dispatch'
|
||||||
|
run: |-
|
||||||
|
mkdir -p dist/release
|
||||||
|
cp clients/android/app/build/outputs/apk/play/release/*.apk dist/release
|
||||||
|
cp clients/android/app/build/outputs/apk/other/release/*-universal.apk dist/release
|
||||||
|
- name: Upload artifact
|
||||||
|
if: github.event_name == 'workflow_dispatch'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: binary-android-apks
|
||||||
|
path: 'dist'
|
||||||
|
publish_android:
|
||||||
|
name: Publish Android
|
||||||
|
if: github.event_name == 'workflow_dispatch' && inputs.build == 'publish-android'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- calculate_version
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ^1.23
|
||||||
|
- name: Setup Android NDK
|
||||||
|
id: setup-ndk
|
||||||
|
uses: nttld/setup-ndk@v1
|
||||||
|
with:
|
||||||
|
ndk-version: r28-beta2
|
||||||
|
- name: Setup OpenJDK
|
||||||
|
run: |-
|
||||||
|
sudo apt update && sudo apt install -y openjdk-17-jdk-headless
|
||||||
|
/usr/lib/jvm/java-17-openjdk-amd64/bin/java --version
|
||||||
|
- name: Set tag
|
||||||
|
run: |-
|
||||||
|
git tag v${{ needs.calculate_version.outputs.version }}
|
||||||
|
- name: Build library
|
||||||
|
run: |-
|
||||||
|
make lib_install
|
||||||
|
export PATH="$PATH:$(go env GOPATH)/bin"
|
||||||
|
make lib_android
|
||||||
|
env:
|
||||||
|
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
|
||||||
|
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||||
|
- name: Checkout main branch
|
||||||
|
if: needs.calculate_version.outputs.prerelease == 'false'
|
||||||
|
run: |-
|
||||||
|
cd clients/android
|
||||||
|
git checkout main
|
||||||
|
- name: Checkout dev branch
|
||||||
|
if: needs.calculate_version.outputs.prerelease == 'true'
|
||||||
|
run: |-
|
||||||
|
cd clients/android
|
||||||
|
git checkout dev
|
||||||
|
- name: Gradle cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.gradle
|
||||||
|
key: gradle-${{ hashFiles('**/*.gradle') }}
|
||||||
|
- name: Build
|
||||||
|
run: |-
|
||||||
|
go run -v ./cmd/internal/update_android_version --ci
|
||||||
|
mkdir clients/android/app/libs
|
||||||
|
cp libbox.aar clients/android/app/libs
|
||||||
|
cd clients/android
|
||||||
|
echo -n "$SERVICE_ACCOUNT_CREDENTIALS" | base64 --decode > service-account-credentials.json
|
||||||
|
./gradlew :app:publishPlayReleaseBundle
|
||||||
|
env:
|
||||||
|
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
|
||||||
|
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||||
|
LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }}
|
||||||
|
SERVICE_ACCOUNT_CREDENTIALS: ${{ secrets.SERVICE_ACCOUNT_CREDENTIALS }}
|
||||||
|
build_apple_library:
|
||||||
|
name: Build Apple library
|
||||||
|
if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store' || inputs.build == 'iOS' || inputs.build == 'macOS' || inputs.build == 'tvOS' || inputs.build == 'macOS-standalone'
|
||||||
|
runs-on: macos-15
|
||||||
|
needs:
|
||||||
|
- calculate_version
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ^1.23
|
||||||
|
- name: Setup Xcode
|
||||||
|
run: |-
|
||||||
|
sudo xcode-select -s /Applications/Xcode_16.2_beta_3.app
|
||||||
|
- name: Set tag
|
||||||
|
run: |-
|
||||||
|
git tag v${{ needs.calculate_version.outputs.version }}
|
||||||
|
- name: Build library
|
||||||
|
run: |-
|
||||||
|
make lib_install
|
||||||
|
export PATH="$PATH:$(go env GOPATH)/bin"
|
||||||
|
make lib_ios
|
||||||
|
- name: Upload library
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: library-apple
|
||||||
|
path: 'Libbox.xcframework'
|
||||||
|
build_apple:
|
||||||
|
name: Build Apple clients
|
||||||
|
runs-on: macos-15
|
||||||
|
needs:
|
||||||
|
- calculate_version
|
||||||
|
- build_apple_library
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- name: iOS
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'iOS' }}
|
||||||
|
scheme: SFI
|
||||||
|
destination: 'generic/platform=iOS'
|
||||||
|
archive: build/SFI.xcarchive
|
||||||
|
upload: SFI/Upload.plist
|
||||||
|
- name: macOS
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'macOS' }}
|
||||||
|
scheme: SFM
|
||||||
|
destination: 'generic/platform=macOS'
|
||||||
|
archive: build/SFM.xcarchive
|
||||||
|
upload: SFI/Upload.plist
|
||||||
|
- name: tvOS
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'tvOS' }}
|
||||||
|
scheme: SFT
|
||||||
|
destination: 'generic/platform=tvOS'
|
||||||
|
archive: build/SFT.xcarchive
|
||||||
|
upload: SFI/Upload.plist
|
||||||
|
- name: macOS-standalone
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'macOS-standalone' }}
|
||||||
|
scheme: SFM.System
|
||||||
|
destination: 'generic/platform=macOS'
|
||||||
|
archive: build/SFM.System.xcarchive
|
||||||
|
export: SFM.System/Export.plist
|
||||||
|
export_path: build/SFM.System
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
if: matrix.if
|
||||||
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
submodules: 'recursive'
|
||||||
|
- name: Setup Go
|
||||||
|
if: matrix.if
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ^1.23
|
||||||
|
- name: Setup Xcode
|
||||||
|
if: matrix.if
|
||||||
|
run: |-
|
||||||
|
sudo xcode-select -s /Applications/Xcode_16.2_beta_3.app
|
||||||
|
- name: Set tag
|
||||||
|
if: matrix.if
|
||||||
|
run: |-
|
||||||
|
git tag v${{ needs.calculate_version.outputs.version }}
|
||||||
|
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
|
||||||
|
- name: Checkout main branch
|
||||||
|
if: matrix.if && needs.calculate_version.outputs.prerelease == 'false'
|
||||||
|
run: |-
|
||||||
|
cd clients/apple
|
||||||
|
git checkout main
|
||||||
|
- name: Checkout dev branch
|
||||||
|
if: matrix.if && needs.calculate_version.outputs.prerelease == 'true'
|
||||||
|
run: |-
|
||||||
|
cd clients/apple
|
||||||
|
git checkout dev
|
||||||
|
- name: Setup certificates
|
||||||
|
if: matrix.if
|
||||||
|
run: |-
|
||||||
|
CERTIFICATE_PATH=$RUNNER_TEMP/Certificates.p12
|
||||||
|
KEYCHAIN_PATH=$RUNNER_TEMP/certificates.keychain-db
|
||||||
|
echo -n "$CERTIFICATES_P12" | base64 --decode -o $CERTIFICATE_PATH
|
||||||
|
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||||
|
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
|
||||||
|
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||||
|
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
||||||
|
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||||
|
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||||
|
|
||||||
|
PROFILES_ZIP_PATH=$RUNNER_TEMP/Profiles.zip
|
||||||
|
echo -n "$PROVISIONING_PROFILES" | base64 --decode -o $PROFILES_ZIP_PATH
|
||||||
|
|
||||||
|
PROFILES_PATH="$HOME/Library/MobileDevice/Provisioning Profiles"
|
||||||
|
mkdir -p "$PROFILES_PATH"
|
||||||
|
unzip $PROFILES_ZIP_PATH -d "$PROFILES_PATH"
|
||||||
|
|
||||||
|
ASC_KEY_PATH=$RUNNER_TEMP/Key.p12
|
||||||
|
echo -n "$ASC_KEY" | base64 --decode -o $ASC_KEY_PATH
|
||||||
|
|
||||||
|
xcrun notarytool store-credentials "notarytool-password" \
|
||||||
|
--key $ASC_KEY_PATH \
|
||||||
|
--key-id $ASC_KEY_ID \
|
||||||
|
--issuer $ASC_KEY_ISSUER_ID
|
||||||
|
env:
|
||||||
|
CERTIFICATES_P12: ${{ secrets.CERTIFICATES_P12 }}
|
||||||
|
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
|
||||||
|
KEYCHAIN_PASSWORD: ${{ secrets.P12_PASSWORD }}
|
||||||
|
PROVISIONING_PROFILES: ${{ secrets.PROVISIONING_PROFILES }}
|
||||||
|
ASC_KEY: ${{ secrets.ASC_KEY }}
|
||||||
|
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
|
||||||
|
ASC_KEY_ISSUER_ID: ${{ secrets.ASC_KEY_ISSUER_ID }}
|
||||||
|
- name: Download library
|
||||||
|
if: matrix.if
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: library-apple
|
||||||
|
path: clients/apple/Libbox.xcframework
|
||||||
|
- name: Build
|
||||||
|
if: matrix.if
|
||||||
|
run: |-
|
||||||
|
go run -v ./cmd/internal/update_apple_version --ci
|
||||||
|
cd clients/apple
|
||||||
|
xcodebuild archive \
|
||||||
|
-scheme "${{ matrix.scheme }}" \
|
||||||
|
-configuration Release \
|
||||||
|
-destination "${{ matrix.destination }}" \
|
||||||
|
-archivePath "${{ matrix.archive }}" \
|
||||||
|
-allowProvisioningUpdates \
|
||||||
|
-authenticationKeyPath $RUNNER_TEMP/Key.p12 \
|
||||||
|
-authenticationKeyID $ASC_KEY_ID \
|
||||||
|
-authenticationKeyIssuerID $ASC_KEY_ISSUER_ID
|
||||||
|
env:
|
||||||
|
MACOS_PROJECT_VERSION: ${{ inputs.macos_project_version }}
|
||||||
|
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
|
||||||
|
ASC_KEY_ISSUER_ID: ${{ secrets.ASC_KEY_ISSUER_ID }}
|
||||||
|
- name: Upload to App Store Connect
|
||||||
|
if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch'
|
||||||
|
run: |-
|
||||||
|
cd clients/apple
|
||||||
|
xcodebuild -exportArchive \
|
||||||
|
-archivePath "${{ matrix.archive }}" \
|
||||||
|
-exportOptionsPlist ${{ matrix.upload }} \
|
||||||
|
-allowProvisioningUpdates \
|
||||||
|
-authenticationKeyPath $RUNNER_TEMP/Key.p12 \
|
||||||
|
-authenticationKeyID $ASC_KEY_ID \
|
||||||
|
-authenticationKeyIssuerID $ASC_KEY_ISSUER_ID
|
||||||
|
env:
|
||||||
|
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
|
||||||
|
ASC_KEY_ISSUER_ID: ${{ secrets.ASC_KEY_ISSUER_ID }}
|
||||||
|
- name: Build image
|
||||||
|
if: matrix.if && matrix.name == 'macOS-standalone' && github.event_name == 'workflow_dispatch'
|
||||||
|
run: |-
|
||||||
|
pushd clients/apple
|
||||||
|
xcodebuild -exportArchive \
|
||||||
|
-archivePath "${{ matrix.archive }}" \
|
||||||
|
-exportOptionsPlist ${{ matrix.export }} \
|
||||||
|
-exportPath "${{ matrix.export_path }}"
|
||||||
|
brew install create-dmg
|
||||||
|
create-dmg \
|
||||||
|
--volname "sing-box" \
|
||||||
|
--volicon "${{ matrix.export_path }}/SFM.app/Contents/Resources/AppIcon.icns" \
|
||||||
|
--icon "SFM.app" 0 0 \
|
||||||
|
--hide-extension "SFM.app" \
|
||||||
|
--app-drop-link 0 0 \
|
||||||
|
--skip-jenkins \
|
||||||
|
SFM.dmg "${{ matrix.export_path }}/SFM.app"
|
||||||
|
xcrun notarytool submit "SFM.dmg" --wait --keychain-profile "notarytool-password"
|
||||||
|
cd "${{ matrix.archive }}"
|
||||||
|
zip -r SFM.dSYMs.zip dSYMs
|
||||||
|
popd
|
||||||
|
|
||||||
|
mkdir -p dist/release
|
||||||
|
cp clients/apple/SFM.dmg "dist/release/SFM-${VERSION}-universal.dmg"
|
||||||
|
cp "clients/apple/${{ matrix.archive }}/SFM.dSYMs.zip" "dist/release/SFM-${VERSION}-universal.dSYMs.zip"
|
||||||
|
- name: Upload image
|
||||||
|
if: matrix.if && matrix.name == 'macOS-standalone' && github.event_name == 'workflow_dispatch'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: binary-macos-dmg
|
||||||
|
path: 'dist'
|
||||||
|
upload:
|
||||||
|
name: Upload builds
|
||||||
|
if: always() && github.event_name == 'workflow_dispatch' && inputs.build != 'publish-android'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- calculate_version
|
||||||
|
- build
|
||||||
|
- build_android
|
||||||
|
- build_apple
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Setup Goreleaser
|
||||||
|
uses: goreleaser/goreleaser-action@v6
|
||||||
|
with:
|
||||||
|
distribution: goreleaser-pro
|
||||||
|
version: latest
|
||||||
|
install-only: true
|
||||||
|
- name: Cache ghr
|
||||||
|
uses: actions/cache@v4
|
||||||
|
id: cache-ghr
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/go/bin/ghr
|
||||||
|
key: ghr
|
||||||
|
- name: Setup ghr
|
||||||
|
if: steps.cache-ghr.outputs.cache-hit != 'true'
|
||||||
|
run: |-
|
||||||
|
cd $HOME
|
||||||
|
git clone https://github.com/nekohasekai/ghr ghr
|
||||||
|
cd ghr
|
||||||
|
go install -v .
|
||||||
|
- name: Set tag
|
||||||
|
run: |-
|
||||||
|
git tag v${{ needs.calculate_version.outputs.version }}
|
||||||
|
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
|
||||||
|
- name: Download builds
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: dist
|
||||||
|
merge-multiple: true
|
||||||
|
- name: Merge builds
|
||||||
|
if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary'
|
||||||
|
run: |-
|
||||||
|
goreleaser continue --merge --skip publish
|
||||||
|
mkdir -p dist/release
|
||||||
|
mv dist/*/sing-box*{tar.gz,zip,deb,rpm,_amd64.pkg.tar.zst,_arm64.pkg.tar.zst} dist/release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||||
|
- name: Upload builds
|
||||||
|
run: |-
|
||||||
|
export PATH="$PATH:$HOME/go/bin"
|
||||||
|
ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist/release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
219
.github/workflows/debug.yml
vendored
219
.github/workflows/debug.yml
vendored
@@ -1,219 +0,0 @@
|
|||||||
name: Debug build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- stable-next
|
|
||||||
- main-next
|
|
||||||
- dev-next
|
|
||||||
paths-ignore:
|
|
||||||
- '**.md'
|
|
||||||
- '.github/**'
|
|
||||||
- '!.github/workflows/debug.yml'
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- stable-next
|
|
||||||
- main-next
|
|
||||||
- dev-next
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Debug build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ^1.23
|
|
||||||
- name: Run Test
|
|
||||||
run: |
|
|
||||||
go test -v ./...
|
|
||||||
build_go120:
|
|
||||||
name: Debug build (Go 1.20)
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ~1.20
|
|
||||||
- name: Cache go module
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/go/pkg/mod
|
|
||||||
key: go120-${{ hashFiles('**/go.sum') }}
|
|
||||||
- name: Run Test
|
|
||||||
run: make ci_build_go120
|
|
||||||
build_go121:
|
|
||||||
name: Debug build (Go 1.21)
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ~1.21
|
|
||||||
- name: Cache go module
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/go/pkg/mod
|
|
||||||
key: go121-${{ hashFiles('**/go.sum') }}
|
|
||||||
- name: Run Test
|
|
||||||
run: make ci_build
|
|
||||||
build_go122:
|
|
||||||
name: Debug build (Go 1.22)
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ~1.22
|
|
||||||
- name: Cache go module
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/go/pkg/mod
|
|
||||||
key: go122-${{ hashFiles('**/go.sum') }}
|
|
||||||
- name: Run Test
|
|
||||||
run: make ci_build
|
|
||||||
cross:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# windows
|
|
||||||
- name: windows-amd64
|
|
||||||
goos: windows
|
|
||||||
goarch: amd64
|
|
||||||
goamd64: v1
|
|
||||||
- name: windows-amd64-v3
|
|
||||||
goos: windows
|
|
||||||
goarch: amd64
|
|
||||||
goamd64: v3
|
|
||||||
- name: windows-386
|
|
||||||
goos: windows
|
|
||||||
goarch: 386
|
|
||||||
- name: windows-arm64
|
|
||||||
goos: windows
|
|
||||||
goarch: arm64
|
|
||||||
- name: windows-arm32v7
|
|
||||||
goos: windows
|
|
||||||
goarch: arm
|
|
||||||
goarm: 7
|
|
||||||
|
|
||||||
# linux
|
|
||||||
- name: linux-amd64
|
|
||||||
goos: linux
|
|
||||||
goarch: amd64
|
|
||||||
goamd64: v1
|
|
||||||
- name: linux-amd64-v3
|
|
||||||
goos: linux
|
|
||||||
goarch: amd64
|
|
||||||
goamd64: v3
|
|
||||||
- name: linux-386
|
|
||||||
goos: linux
|
|
||||||
goarch: 386
|
|
||||||
- name: linux-arm64
|
|
||||||
goos: linux
|
|
||||||
goarch: arm64
|
|
||||||
- name: linux-armv5
|
|
||||||
goos: linux
|
|
||||||
goarch: arm
|
|
||||||
goarm: 5
|
|
||||||
- name: linux-armv6
|
|
||||||
goos: linux
|
|
||||||
goarch: arm
|
|
||||||
goarm: 6
|
|
||||||
- name: linux-armv7
|
|
||||||
goos: linux
|
|
||||||
goarch: arm
|
|
||||||
goarm: 7
|
|
||||||
- name: linux-mips-softfloat
|
|
||||||
goos: linux
|
|
||||||
goarch: mips
|
|
||||||
gomips: softfloat
|
|
||||||
- name: linux-mips-hardfloat
|
|
||||||
goos: linux
|
|
||||||
goarch: mips
|
|
||||||
gomips: hardfloat
|
|
||||||
- name: linux-mipsel-softfloat
|
|
||||||
goos: linux
|
|
||||||
goarch: mipsle
|
|
||||||
gomips: softfloat
|
|
||||||
- name: linux-mipsel-hardfloat
|
|
||||||
goos: linux
|
|
||||||
goarch: mipsle
|
|
||||||
gomips: hardfloat
|
|
||||||
- name: linux-mips64
|
|
||||||
goos: linux
|
|
||||||
goarch: mips64
|
|
||||||
- name: linux-mips64el
|
|
||||||
goos: linux
|
|
||||||
goarch: mips64le
|
|
||||||
- name: linux-s390x
|
|
||||||
goos: linux
|
|
||||||
goarch: s390x
|
|
||||||
# darwin
|
|
||||||
- name: darwin-amd64
|
|
||||||
goos: darwin
|
|
||||||
goarch: amd64
|
|
||||||
goamd64: v1
|
|
||||||
- name: darwin-amd64-v3
|
|
||||||
goos: darwin
|
|
||||||
goarch: amd64
|
|
||||||
goamd64: v3
|
|
||||||
- name: darwin-arm64
|
|
||||||
goos: darwin
|
|
||||||
goarch: arm64
|
|
||||||
# freebsd
|
|
||||||
- name: freebsd-amd64
|
|
||||||
goos: freebsd
|
|
||||||
goarch: amd64
|
|
||||||
goamd64: v1
|
|
||||||
- name: freebsd-amd64-v3
|
|
||||||
goos: freebsd
|
|
||||||
goarch: amd64
|
|
||||||
goamd64: v3
|
|
||||||
- name: freebsd-386
|
|
||||||
goos: freebsd
|
|
||||||
goarch: 386
|
|
||||||
- name: freebsd-arm64
|
|
||||||
goos: freebsd
|
|
||||||
goarch: arm64
|
|
||||||
fail-fast: true
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
GOOS: ${{ matrix.goos }}
|
|
||||||
GOARCH: ${{ matrix.goarch }}
|
|
||||||
GOAMD64: ${{ matrix.goamd64 }}
|
|
||||||
GOARM: ${{ matrix.goarm }}
|
|
||||||
GOMIPS: ${{ matrix.gomips }}
|
|
||||||
CGO_ENABLED: 0
|
|
||||||
TAGS: with_clash_api,with_quic
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ^1.21
|
|
||||||
- name: Build
|
|
||||||
id: build
|
|
||||||
run: make
|
|
||||||
1
.github/workflows/linux.yml
vendored
1
.github/workflows/linux.yml
vendored
@@ -22,7 +22,6 @@ jobs:
|
|||||||
mkdir -p $HOME/.gnupg
|
mkdir -p $HOME/.gnupg
|
||||||
cat > $HOME/.gnupg/sagernet.key <<EOF
|
cat > $HOME/.gnupg/sagernet.key <<EOF
|
||||||
${{ secrets.GPG_KEY }}
|
${{ secrets.GPG_KEY }}
|
||||||
echo "HOME=$HOME" >> "$GITHUB_ENV"
|
|
||||||
EOF
|
EOF
|
||||||
echo "HOME=$HOME" >> "$GITHUB_ENV"
|
echo "HOME=$HOME" >> "$GITHUB_ENV"
|
||||||
- name: Publish release
|
- name: Publish release
|
||||||
|
|||||||
@@ -200,4 +200,6 @@ release:
|
|||||||
ids:
|
ids:
|
||||||
- archive
|
- archive
|
||||||
- package
|
- package
|
||||||
skip_upload: true
|
skip_upload: true
|
||||||
|
partial:
|
||||||
|
by: target
|
||||||
12
Makefile
12
Makefile
@@ -71,7 +71,7 @@ release:
|
|||||||
dist/*_amd64.pkg.tar.zst \
|
dist/*_amd64.pkg.tar.zst \
|
||||||
dist/*_arm64.pkg.tar.zst \
|
dist/*_arm64.pkg.tar.zst \
|
||||||
dist/release
|
dist/release
|
||||||
ghr --replace --draft --prerelease -p 3 "v${VERSION}" dist/release
|
ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist/release
|
||||||
rm -r dist/release
|
rm -r dist/release
|
||||||
|
|
||||||
release_repo:
|
release_repo:
|
||||||
@@ -90,7 +90,7 @@ upload_android:
|
|||||||
mkdir -p dist/release_android
|
mkdir -p dist/release_android
|
||||||
cp ../sing-box-for-android/app/build/outputs/apk/play/release/*.apk dist/release_android
|
cp ../sing-box-for-android/app/build/outputs/apk/play/release/*.apk dist/release_android
|
||||||
cp ../sing-box-for-android/app/build/outputs/apk/other/release/*-universal.apk dist/release_android
|
cp ../sing-box-for-android/app/build/outputs/apk/other/release/*-universal.apk dist/release_android
|
||||||
ghr --replace --draft --prerelease -p 3 "v${VERSION}" dist/release_android
|
ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist/release_android
|
||||||
rm -rf dist/release_android
|
rm -rf dist/release_android
|
||||||
|
|
||||||
release_android: lib_android update_android_version build_android upload_android
|
release_android: lib_android update_android_version build_android upload_android
|
||||||
@@ -99,9 +99,11 @@ publish_android:
|
|||||||
cd ../sing-box-for-android && ./gradlew :app:publishPlayReleaseBundle && ./gradlew --stop
|
cd ../sing-box-for-android && ./gradlew :app:publishPlayReleaseBundle && ./gradlew --stop
|
||||||
|
|
||||||
# TODO: find why and remove `-destination 'generic/platform=iOS'`
|
# TODO: find why and remove `-destination 'generic/platform=iOS'`
|
||||||
|
# TODO: remove xcode clean when fix control widget fixed
|
||||||
build_ios:
|
build_ios:
|
||||||
cd ../sing-box-for-apple && \
|
cd ../sing-box-for-apple && \
|
||||||
rm -rf build/SFI.xcarchive && \
|
rm -rf build/SFI.xcarchive && \
|
||||||
|
xcodebuild clean -scheme SFI && \
|
||||||
xcodebuild archive -scheme SFI -configuration Release -destination 'generic/platform=iOS' -archivePath build/SFI.xcarchive -allowProvisioningUpdates
|
xcodebuild archive -scheme SFI -configuration Release -destination 'generic/platform=iOS' -archivePath build/SFI.xcarchive -allowProvisioningUpdates
|
||||||
|
|
||||||
upload_ios_app_store:
|
upload_ios_app_store:
|
||||||
@@ -199,9 +201,15 @@ test_stdio:
|
|||||||
lib_android:
|
lib_android:
|
||||||
go run ./cmd/internal/build_libbox -target android
|
go run ./cmd/internal/build_libbox -target android
|
||||||
|
|
||||||
|
lib_android_debug:
|
||||||
|
go run ./cmd/internal/build_libbox -target android -debug
|
||||||
|
|
||||||
lib_ios:
|
lib_ios:
|
||||||
go run ./cmd/internal/build_libbox -target ios
|
go run ./cmd/internal/build_libbox -target ios
|
||||||
|
|
||||||
|
lib_ios_debug:
|
||||||
|
go run ./cmd/internal/build_libbox -target ios -debug
|
||||||
|
|
||||||
lib:
|
lib:
|
||||||
go run ./cmd/internal/build_libbox -target android
|
go run ./cmd/internal/build_libbox -target android
|
||||||
go run ./cmd/internal/build_libbox -target ios
|
go run ./cmd/internal/build_libbox -target ios
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ConnectionManager interface {
|
type ConnectionManager interface {
|
||||||
Start() error
|
Lifecycle
|
||||||
Close() error
|
|
||||||
NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
||||||
NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ type PacketConnectionHandlerEx interface {
|
|||||||
NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: use TCPConnectionHandlerEx instead
|
||||||
|
//
|
||||||
|
//nolint:staticcheck
|
||||||
type UpstreamHandlerAdapter interface {
|
type UpstreamHandlerAdapter interface {
|
||||||
N.TCPConnectionHandler
|
N.TCPConnectionHandler
|
||||||
N.UDPConnectionHandler
|
N.UDPConnectionHandler
|
||||||
|
|||||||
@@ -65,14 +65,17 @@ type InboundContext struct {
|
|||||||
LastInbound string
|
LastInbound string
|
||||||
OriginDestination M.Socksaddr
|
OriginDestination M.Socksaddr
|
||||||
RouteOriginalDestination M.Socksaddr
|
RouteOriginalDestination M.Socksaddr
|
||||||
// Deprecated
|
// Deprecated: to be removed
|
||||||
|
//nolint:staticcheck
|
||||||
InboundOptions option.InboundOptions
|
InboundOptions option.InboundOptions
|
||||||
UDPDisableDomainUnmapping bool
|
UDPDisableDomainUnmapping bool
|
||||||
UDPConnect bool
|
UDPConnect bool
|
||||||
NetworkStrategy C.NetworkStrategy
|
UDPTimeout time.Duration
|
||||||
NetworkType []C.InterfaceType
|
|
||||||
FallbackNetworkType []C.InterfaceType
|
NetworkStrategy C.NetworkStrategy
|
||||||
FallbackDelay time.Duration
|
NetworkType []C.InterfaceType
|
||||||
|
FallbackNetworkType []C.InterfaceType
|
||||||
|
FallbackDelay time.Duration
|
||||||
|
|
||||||
DNSServer string
|
DNSServer string
|
||||||
|
|
||||||
|
|||||||
@@ -1,157 +0,0 @@
|
|||||||
package outbound
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
"github.com/sagernet/sing/common/canceler"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error {
|
|
||||||
defer conn.Close()
|
|
||||||
ctx = adapter.WithContext(ctx, &metadata)
|
|
||||||
var outConn net.Conn
|
|
||||||
var err error
|
|
||||||
if len(metadata.DestinationAddresses) > 0 {
|
|
||||||
outConn, err = dialer.DialSerialNetwork(ctx, this, N.NetworkTCP, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.NetworkType, metadata.FallbackNetworkType, metadata.FallbackDelay)
|
|
||||||
} else {
|
|
||||||
outConn, err = this.DialContext(ctx, N.NetworkTCP, metadata.Destination)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return N.ReportHandshakeFailure(conn, err)
|
|
||||||
}
|
|
||||||
err = N.ReportConnHandshakeSuccess(conn, outConn)
|
|
||||||
if err != nil {
|
|
||||||
outConn.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return CopyEarlyConn(ctx, conn, outConn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error {
|
|
||||||
defer conn.Close()
|
|
||||||
ctx = adapter.WithContext(ctx, &metadata)
|
|
||||||
var (
|
|
||||||
outPacketConn net.PacketConn
|
|
||||||
outConn net.Conn
|
|
||||||
destinationAddress netip.Addr
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
if metadata.UDPConnect {
|
|
||||||
if len(metadata.DestinationAddresses) > 0 {
|
|
||||||
if parallelDialer, isParallelDialer := this.(dialer.ParallelInterfaceDialer); isParallelDialer {
|
|
||||||
outConn, err = dialer.DialSerialNetwork(ctx, parallelDialer, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.NetworkType, metadata.FallbackNetworkType, metadata.FallbackDelay)
|
|
||||||
} else {
|
|
||||||
outConn, err = N.DialSerial(ctx, this, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
outConn, err = this.DialContext(ctx, N.NetworkUDP, metadata.Destination)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return N.ReportHandshakeFailure(conn, err)
|
|
||||||
}
|
|
||||||
outPacketConn = bufio.NewUnbindPacketConn(outConn)
|
|
||||||
connRemoteAddr := M.AddrFromNet(outConn.RemoteAddr())
|
|
||||||
if connRemoteAddr != metadata.Destination.Addr {
|
|
||||||
destinationAddress = connRemoteAddr
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if len(metadata.DestinationAddresses) > 0 {
|
|
||||||
outPacketConn, destinationAddress, err = dialer.ListenSerialNetworkPacket(ctx, this, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.NetworkType, metadata.FallbackNetworkType, metadata.FallbackDelay)
|
|
||||||
} else {
|
|
||||||
outPacketConn, err = this.ListenPacket(ctx, metadata.Destination)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return N.ReportHandshakeFailure(conn, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = N.ReportPacketConnHandshakeSuccess(conn, outPacketConn)
|
|
||||||
if err != nil {
|
|
||||||
outPacketConn.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if destinationAddress.IsValid() {
|
|
||||||
var originDestination M.Socksaddr
|
|
||||||
if metadata.RouteOriginalDestination.IsValid() {
|
|
||||||
originDestination = metadata.RouteOriginalDestination
|
|
||||||
} else {
|
|
||||||
originDestination = metadata.Destination
|
|
||||||
}
|
|
||||||
if metadata.Destination != M.SocksaddrFrom(destinationAddress, metadata.Destination.Port) {
|
|
||||||
if metadata.UDPDisableDomainUnmapping {
|
|
||||||
outPacketConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(outPacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), originDestination)
|
|
||||||
} else {
|
|
||||||
outPacketConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outPacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), originDestination)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded {
|
|
||||||
natConn.UpdateDestination(destinationAddress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch metadata.Protocol {
|
|
||||||
case C.ProtocolSTUN:
|
|
||||||
ctx, conn = canceler.NewPacketConn(ctx, conn, C.STUNTimeout)
|
|
||||||
case C.ProtocolQUIC:
|
|
||||||
ctx, conn = canceler.NewPacketConn(ctx, conn, C.QUICTimeout)
|
|
||||||
case C.ProtocolDNS:
|
|
||||||
ctx, conn = canceler.NewPacketConn(ctx, conn, C.DNSTimeout)
|
|
||||||
}
|
|
||||||
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outPacketConn))
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) error {
|
|
||||||
if cachedReader, isCached := conn.(N.CachedReader); isCached {
|
|
||||||
payload := cachedReader.ReadCached()
|
|
||||||
if payload != nil && !payload.IsEmpty() {
|
|
||||||
_, err := serverConn.Write(payload.Bytes())
|
|
||||||
payload.Release()
|
|
||||||
if err != nil {
|
|
||||||
serverConn.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return bufio.CopyConn(ctx, conn, serverConn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if earlyConn, isEarlyConn := common.Cast[N.EarlyConn](serverConn); isEarlyConn && earlyConn.NeedHandshake() {
|
|
||||||
payload := buf.NewPacket()
|
|
||||||
err := conn.SetReadDeadline(time.Now().Add(C.ReadPayloadTimeout))
|
|
||||||
if err != os.ErrInvalid {
|
|
||||||
if err != nil {
|
|
||||||
payload.Release()
|
|
||||||
serverConn.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = payload.ReadOnceFrom(conn)
|
|
||||||
if err != nil && !E.IsTimeout(err) {
|
|
||||||
payload.Release()
|
|
||||||
serverConn.Close()
|
|
||||||
return E.Cause(err, "read payload")
|
|
||||||
}
|
|
||||||
err = conn.SetReadDeadline(time.Time{})
|
|
||||||
if err != nil {
|
|
||||||
payload.Release()
|
|
||||||
serverConn.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err = serverConn.Write(payload.Bytes())
|
|
||||||
payload.Release()
|
|
||||||
if err != nil {
|
|
||||||
serverConn.Close()
|
|
||||||
return N.ReportHandshakeFailure(conn, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bufio.CopyConn(ctx, conn, serverConn)
|
|
||||||
}
|
|
||||||
@@ -58,6 +58,13 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
|||||||
outbounds := m.outbounds
|
outbounds := m.outbounds
|
||||||
m.access.Unlock()
|
m.access.Unlock()
|
||||||
if stage == adapter.StartStateStart {
|
if stage == adapter.StartStateStart {
|
||||||
|
if m.defaultTag != "" && m.defaultOutbound == nil {
|
||||||
|
defaultEndpoint, loaded := m.endpoint.Get(m.defaultTag)
|
||||||
|
if !loaded {
|
||||||
|
return E.New("default outbound not found: ", m.defaultTag)
|
||||||
|
}
|
||||||
|
m.defaultOutbound = defaultEndpoint
|
||||||
|
}
|
||||||
return m.startOutbounds(append(outbounds, common.Map(m.endpoint.Endpoints(), func(it adapter.Endpoint) adapter.Outbound { return it })...))
|
return m.startOutbounds(append(outbounds, common.Map(m.endpoint.Endpoints(), func(it adapter.Endpoint) adapter.Outbound { return it })...))
|
||||||
} else {
|
} else {
|
||||||
for _, outbound := range outbounds {
|
for _, outbound := range outbounds {
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ type (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated
|
||||||
|
//
|
||||||
|
//nolint:staticcheck
|
||||||
func NewUpstreamHandler(
|
func NewUpstreamHandler(
|
||||||
metadata InboundContext,
|
metadata InboundContext,
|
||||||
connectionHandler ConnectionHandlerFunc,
|
connectionHandler ConnectionHandlerFunc,
|
||||||
@@ -34,7 +36,9 @@ func NewUpstreamHandler(
|
|||||||
|
|
||||||
var _ UpstreamHandlerAdapter = (*myUpstreamHandlerWrapper)(nil)
|
var _ UpstreamHandlerAdapter = (*myUpstreamHandlerWrapper)(nil)
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated: use myUpstreamHandlerWrapperEx instead.
|
||||||
|
//
|
||||||
|
//nolint:staticcheck
|
||||||
type myUpstreamHandlerWrapper struct {
|
type myUpstreamHandlerWrapper struct {
|
||||||
metadata InboundContext
|
metadata InboundContext
|
||||||
connectionHandler ConnectionHandlerFunc
|
connectionHandler ConnectionHandlerFunc
|
||||||
@@ -42,6 +46,7 @@ type myUpstreamHandlerWrapper struct {
|
|||||||
errorHandler E.Handler
|
errorHandler E.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: use myUpstreamHandlerWrapperEx instead.
|
||||||
func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
myMetadata := w.metadata
|
myMetadata := w.metadata
|
||||||
if metadata.Source.IsValid() {
|
if metadata.Source.IsValid() {
|
||||||
@@ -53,6 +58,7 @@ func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.C
|
|||||||
return w.connectionHandler(ctx, conn, myMetadata)
|
return w.connectionHandler(ctx, conn, myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: use myUpstreamHandlerWrapperEx instead.
|
||||||
func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||||
myMetadata := w.metadata
|
myMetadata := w.metadata
|
||||||
if metadata.Source.IsValid() {
|
if metadata.Source.IsValid() {
|
||||||
@@ -64,11 +70,12 @@ func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn
|
|||||||
return w.packetHandler(ctx, conn, myMetadata)
|
return w.packetHandler(ctx, conn, myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: use myUpstreamHandlerWrapperEx instead.
|
||||||
func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
|
func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
|
||||||
w.errorHandler.NewError(ctx, err)
|
w.errorHandler.NewError(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated: removed
|
||||||
func UpstreamMetadata(metadata InboundContext) M.Metadata {
|
func UpstreamMetadata(metadata InboundContext) M.Metadata {
|
||||||
return M.Metadata{
|
return M.Metadata{
|
||||||
Source: metadata.Source,
|
Source: metadata.Source,
|
||||||
@@ -76,14 +83,14 @@ func UpstreamMetadata(metadata InboundContext) M.Metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated: Use NewUpstreamContextHandlerEx instead.
|
||||||
type myUpstreamContextHandlerWrapper struct {
|
type myUpstreamContextHandlerWrapper struct {
|
||||||
connectionHandler ConnectionHandlerFunc
|
connectionHandler ConnectionHandlerFunc
|
||||||
packetHandler PacketConnectionHandlerFunc
|
packetHandler PacketConnectionHandlerFunc
|
||||||
errorHandler E.Handler
|
errorHandler E.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated: Use NewUpstreamContextHandlerEx instead.
|
||||||
func NewUpstreamContextHandler(
|
func NewUpstreamContextHandler(
|
||||||
connectionHandler ConnectionHandlerFunc,
|
connectionHandler ConnectionHandlerFunc,
|
||||||
packetHandler PacketConnectionHandlerFunc,
|
packetHandler PacketConnectionHandlerFunc,
|
||||||
@@ -96,6 +103,7 @@ func NewUpstreamContextHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewUpstreamContextHandlerEx instead.
|
||||||
func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
myMetadata := ContextFrom(ctx)
|
myMetadata := ContextFrom(ctx)
|
||||||
if metadata.Source.IsValid() {
|
if metadata.Source.IsValid() {
|
||||||
@@ -107,6 +115,7 @@ func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, con
|
|||||||
return w.connectionHandler(ctx, conn, *myMetadata)
|
return w.connectionHandler(ctx, conn, *myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewUpstreamContextHandlerEx instead.
|
||||||
func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||||
myMetadata := ContextFrom(ctx)
|
myMetadata := ContextFrom(ctx)
|
||||||
if metadata.Source.IsValid() {
|
if metadata.Source.IsValid() {
|
||||||
@@ -118,6 +127,7 @@ func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Contex
|
|||||||
return w.packetHandler(ctx, conn, *myMetadata)
|
return w.packetHandler(ctx, conn, *myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewUpstreamContextHandlerEx instead.
|
||||||
func (w *myUpstreamContextHandlerWrapper) NewError(ctx context.Context, err error) {
|
func (w *myUpstreamContextHandlerWrapper) NewError(ctx context.Context, err error) {
|
||||||
w.errorHandler.NewError(ctx, err)
|
w.errorHandler.NewError(ctx, err)
|
||||||
}
|
}
|
||||||
@@ -149,12 +159,15 @@ func NewRouteContextHandler(
|
|||||||
var _ UpstreamHandlerAdapter = (*routeHandlerWrapper)(nil)
|
var _ UpstreamHandlerAdapter = (*routeHandlerWrapper)(nil)
|
||||||
|
|
||||||
// Deprecated: Use ConnectionRouterEx instead.
|
// Deprecated: Use ConnectionRouterEx instead.
|
||||||
|
//
|
||||||
|
//nolint:staticcheck
|
||||||
type routeHandlerWrapper struct {
|
type routeHandlerWrapper struct {
|
||||||
metadata InboundContext
|
metadata InboundContext
|
||||||
router ConnectionRouter
|
router ConnectionRouter
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ConnectionRouterEx instead.
|
||||||
func (w *routeHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
func (w *routeHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
myMetadata := w.metadata
|
myMetadata := w.metadata
|
||||||
if metadata.Source.IsValid() {
|
if metadata.Source.IsValid() {
|
||||||
@@ -166,6 +179,7 @@ func (w *routeHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn,
|
|||||||
return w.router.RouteConnection(ctx, conn, myMetadata)
|
return w.router.RouteConnection(ctx, conn, myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ConnectionRouterEx instead.
|
||||||
func (w *routeHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
func (w *routeHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||||
myMetadata := w.metadata
|
myMetadata := w.metadata
|
||||||
if metadata.Source.IsValid() {
|
if metadata.Source.IsValid() {
|
||||||
@@ -177,6 +191,7 @@ func (w *routeHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.Pa
|
|||||||
return w.router.RoutePacketConnection(ctx, conn, myMetadata)
|
return w.router.RoutePacketConnection(ctx, conn, myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ConnectionRouterEx instead.
|
||||||
func (w *routeHandlerWrapper) NewError(ctx context.Context, err error) {
|
func (w *routeHandlerWrapper) NewError(ctx context.Context, err error) {
|
||||||
w.logger.ErrorContext(ctx, err)
|
w.logger.ErrorContext(ctx, err)
|
||||||
}
|
}
|
||||||
@@ -189,6 +204,7 @@ type routeContextHandlerWrapper struct {
|
|||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ConnectionRouterEx instead.
|
||||||
func (w *routeContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
func (w *routeContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||||
myMetadata := ContextFrom(ctx)
|
myMetadata := ContextFrom(ctx)
|
||||||
if metadata.Source.IsValid() {
|
if metadata.Source.IsValid() {
|
||||||
@@ -200,6 +216,7 @@ func (w *routeContextHandlerWrapper) NewConnection(ctx context.Context, conn net
|
|||||||
return w.router.RouteConnection(ctx, conn, *myMetadata)
|
return w.router.RouteConnection(ctx, conn, *myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ConnectionRouterEx instead.
|
||||||
func (w *routeContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
func (w *routeContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||||
myMetadata := ContextFrom(ctx)
|
myMetadata := ContextFrom(ctx)
|
||||||
if metadata.Source.IsValid() {
|
if metadata.Source.IsValid() {
|
||||||
@@ -211,6 +228,7 @@ func (w *routeContextHandlerWrapper) NewPacketConnection(ctx context.Context, co
|
|||||||
return w.router.RoutePacketConnection(ctx, conn, *myMetadata)
|
return w.router.RoutePacketConnection(ctx, conn, *myMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ConnectionRouterEx instead.
|
||||||
func (w *routeContextHandlerWrapper) NewError(ctx context.Context, err error) {
|
func (w *routeContextHandlerWrapper) NewError(ctx context.Context, err error) {
|
||||||
w.logger.ErrorContext(ctx, err)
|
w.logger.ErrorContext(ctx, err)
|
||||||
}
|
}
|
||||||
|
|||||||
10
box.go
10
box.go
@@ -336,11 +336,11 @@ func (s *Box) preStart() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = adapter.Start(adapter.StartStateInitialize, s.network, s.router, s.outbound, s.inbound, s.endpoint)
|
err = adapter.Start(adapter.StartStateInitialize, s.network, s.connection, s.router, s.outbound, s.inbound, s.endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = adapter.Start(adapter.StartStateStart, s.outbound, s.network, s.router)
|
err = adapter.Start(adapter.StartStateStart, s.outbound, s.network, s.connection, s.router)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -364,7 +364,7 @@ func (s *Box) start() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = adapter.Start(adapter.StartStatePostStart, s.outbound, s.network, s.router, s.inbound, s.endpoint)
|
err = adapter.Start(adapter.StartStatePostStart, s.outbound, s.network, s.connection, s.router, s.inbound, s.endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -372,7 +372,7 @@ func (s *Box) start() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = adapter.Start(adapter.StartStateStarted, s.network, s.router, s.outbound, s.inbound, s.endpoint)
|
err = adapter.Start(adapter.StartStateStarted, s.network, s.connection, s.router, s.outbound, s.inbound, s.endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -391,7 +391,7 @@ func (s *Box) Close() error {
|
|||||||
close(s.done)
|
close(s.done)
|
||||||
}
|
}
|
||||||
err := common.Close(
|
err := common.Close(
|
||||||
s.inbound, s.outbound, s.router, s.network,
|
s.inbound, s.outbound, s.router, s.connection, s.network,
|
||||||
)
|
)
|
||||||
for _, lifecycleService := range s.services {
|
for _, lifecycleService := range s.services {
|
||||||
err = E.Append(err, lifecycleService.Close(), func(err error) error {
|
err = E.Append(err, lifecycleService.Close(), func(err error) error {
|
||||||
|
|||||||
Submodule clients/android updated: ea460ea5d1...cff12c57dd
Submodule clients/apple updated: 286f9717cb...fa107e3b7c
@@ -10,7 +10,9 @@ import (
|
|||||||
_ "github.com/sagernet/gomobile"
|
_ "github.com/sagernet/gomobile"
|
||||||
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/rw"
|
"github.com/sagernet/sing/common/rw"
|
||||||
|
"github.com/sagernet/sing/common/shell"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -62,9 +64,33 @@ func init() {
|
|||||||
func buildAndroid() {
|
func buildAndroid() {
|
||||||
build_shared.FindSDK()
|
build_shared.FindSDK()
|
||||||
|
|
||||||
|
var javaPath string
|
||||||
|
javaHome := os.Getenv("JAVA_HOME")
|
||||||
|
if javaHome == "" {
|
||||||
|
javaPath = "java"
|
||||||
|
} else {
|
||||||
|
javaPath = filepath.Join(javaHome, "bin", "java")
|
||||||
|
}
|
||||||
|
|
||||||
|
javaVersion, err := shell.Exec(javaPath, "--version").ReadOutput()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(E.Cause(err, "check java version"))
|
||||||
|
}
|
||||||
|
if !strings.Contains(javaVersion, "openjdk 17") {
|
||||||
|
log.Fatal("java version should be openjdk 17")
|
||||||
|
}
|
||||||
|
|
||||||
|
var bindTarget string
|
||||||
|
if debugEnabled {
|
||||||
|
bindTarget = "android/arm64"
|
||||||
|
} else {
|
||||||
|
bindTarget = "android"
|
||||||
|
}
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"bind",
|
"bind",
|
||||||
"-v",
|
"-v",
|
||||||
|
"-target", bindTarget,
|
||||||
"-androidapi", "21",
|
"-androidapi", "21",
|
||||||
"-javapkg=io.nekohasekai",
|
"-javapkg=io.nekohasekai",
|
||||||
"-libname=box",
|
"-libname=box",
|
||||||
@@ -86,7 +112,7 @@ func buildAndroid() {
|
|||||||
command := exec.Command(build_shared.GoBinPath+"/gomobile", args...)
|
command := exec.Command(build_shared.GoBinPath+"/gomobile", args...)
|
||||||
command.Stdout = os.Stdout
|
command.Stdout = os.Stdout
|
||||||
command.Stderr = os.Stderr
|
command.Stderr = os.Stderr
|
||||||
err := command.Run()
|
err = command.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -104,10 +130,17 @@ func buildAndroid() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func buildiOS() {
|
func buildiOS() {
|
||||||
|
var bindTarget string
|
||||||
|
if debugEnabled {
|
||||||
|
bindTarget = "ios"
|
||||||
|
} else {
|
||||||
|
bindTarget = "ios,iossimulator,tvos,tvossimulator,macos"
|
||||||
|
}
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"bind",
|
"bind",
|
||||||
"-v",
|
"-v",
|
||||||
"-target", "ios,iossimulator,tvos,tvossimulator,macos",
|
"-target", bindTarget,
|
||||||
"-libname=box",
|
"-libname=box",
|
||||||
}
|
}
|
||||||
if !debugEnabled {
|
if !debugEnabled {
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/rw"
|
"github.com/sagernet/sing/common/rw"
|
||||||
"github.com/sagernet/sing/common/shell"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -42,14 +40,6 @@ func FindSDK() {
|
|||||||
log.Fatal("android NDK not found")
|
log.Fatal("android NDK not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
javaVersion, err := shell.Exec("java", "--version").ReadOutput()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(E.Cause(err, "check java version"))
|
|
||||||
}
|
|
||||||
if !strings.Contains(javaVersion, "openjdk 17") {
|
|
||||||
log.Fatal("java version should be openjdk 17")
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Setenv("ANDROID_HOME", androidSDKPath)
|
os.Setenv("ANDROID_HOME", androidSDKPath)
|
||||||
os.Setenv("ANDROID_SDK_HOME", androidSDKPath)
|
os.Setenv("ANDROID_SDK_HOME", androidSDKPath)
|
||||||
os.Setenv("ANDROID_NDK_HOME", androidNDKPath)
|
os.Setenv("ANDROID_NDK_HOME", androidNDKPath)
|
||||||
@@ -58,12 +48,16 @@ func FindSDK() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func findNDK() bool {
|
func findNDK() bool {
|
||||||
const fixedVersion = "26.2.11394342"
|
const fixedVersion = "28.0.12674087"
|
||||||
const versionFile = "source.properties"
|
const versionFile = "source.properties"
|
||||||
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.IsFile(filepath.Join(fixedPath, versionFile)) {
|
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.IsFile(filepath.Join(fixedPath, versionFile)) {
|
||||||
androidNDKPath = fixedPath
|
androidNDKPath = fixedPath
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if ndkHomeEnv := os.Getenv("ANDROID_NDK_HOME"); rw.IsFile(filepath.Join(ndkHomeEnv, versionFile)) {
|
||||||
|
androidNDKPath = ndkHomeEnv
|
||||||
|
return true
|
||||||
|
}
|
||||||
ndkVersions, err := os.ReadDir(filepath.Join(androidSDKPath, "ndk"))
|
ndkVersions, err := os.ReadDir(filepath.Join(androidSDKPath, "ndk"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ func ReadTag() (string, error) {
|
|||||||
return version.String() + "-" + shortCommit, nil
|
return version.String() + "-" + shortCommit, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ReadTagVersionRev() (badversion.Version, error) {
|
||||||
|
currentTagRev := common.Must1(shell.Exec("git", "describe", "--tags", "--abbrev=0").ReadOutput())
|
||||||
|
return badversion.Parse(currentTagRev[1:]), nil
|
||||||
|
}
|
||||||
|
|
||||||
func ReadTagVersion() (badversion.Version, error) {
|
func ReadTagVersion() (badversion.Version, error) {
|
||||||
currentTag := common.Must1(shell.Exec("git", "describe", "--tags").ReadOutput())
|
currentTag := common.Must1(shell.Exec("git", "describe", "--tags").ReadOutput())
|
||||||
currentTagRev := common.Must1(shell.Exec("git", "describe", "--tags", "--abbrev=0").ReadOutput())
|
currentTagRev := common.Must1(shell.Exec("git", "describe", "--tags", "--abbrev=0").ReadOutput())
|
||||||
@@ -31,3 +36,11 @@ func ReadTagVersion() (badversion.Version, error) {
|
|||||||
}
|
}
|
||||||
return version, nil
|
return version, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsDevBranch() bool {
|
||||||
|
branch, err := shell.Exec("git", "branch", "--show-current").ReadOutput()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return branch == "dev-next"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,21 +1,74 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
F "github.com/sagernet/sing/common/format"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var nightly bool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.BoolVar(&nightly, "nightly", false, "Print nightly tag")
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
currentTag, err := build_shared.ReadTag()
|
flag.Parse()
|
||||||
if err != nil {
|
if nightly {
|
||||||
log.Error(err)
|
version, err := build_shared.ReadTagVersionRev()
|
||||||
_, err = os.Stdout.WriteString("unknown\n")
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
versionStr string
|
||||||
|
isPrerelease bool
|
||||||
|
)
|
||||||
|
if version.PreReleaseIdentifier != "" {
|
||||||
|
isPrerelease = true
|
||||||
|
versionStr = version.VersionString() + "-nightly"
|
||||||
|
} else {
|
||||||
|
version.Patch++
|
||||||
|
versionStr = version.VersionString() + "-nightly"
|
||||||
|
}
|
||||||
|
if build_shared.IsDevBranch() {
|
||||||
|
isPrerelease = true
|
||||||
|
}
|
||||||
|
err = setGitHubOutput("version", versionStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
err = setGitHubOutput("prerelease", F.ToString(isPrerelease))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err = os.Stdout.WriteString(currentTag + "\n")
|
tag, err := build_shared.ReadTag()
|
||||||
}
|
if err != nil {
|
||||||
if err != nil {
|
log.Error(err)
|
||||||
log.Error(err)
|
os.Stdout.WriteString("unknown\n")
|
||||||
|
} else {
|
||||||
|
os.Stdout.WriteString(tag + "\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setGitHubOutput(name string, value string) error {
|
||||||
|
outputFile, err := os.OpenFile(os.Getenv("GITHUB_ENV"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = outputFile.WriteString(name + "=" + value + "\n")
|
||||||
|
if err != nil {
|
||||||
|
outputFile.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = outputFile.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
os.Stderr.WriteString(name + "=" + value + "\n")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -12,9 +13,22 @@ import (
|
|||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var flagRunInCI bool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.BoolVar(&flagRunInCI, "ci", false, "Run in CI")
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
newVersion := common.Must1(build_shared.ReadTagVersion())
|
flag.Parse()
|
||||||
androidPath, err := filepath.Abs("../sing-box-for-android")
|
newVersion := common.Must1(build_shared.ReadTag())
|
||||||
|
var androidPath string
|
||||||
|
if flagRunInCI {
|
||||||
|
androidPath = "clients/android"
|
||||||
|
} else {
|
||||||
|
androidPath = "../sing-box-for-android"
|
||||||
|
}
|
||||||
|
androidPath, err := filepath.Abs(androidPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -31,10 +45,10 @@ func main() {
|
|||||||
for _, propPair := range propsList {
|
for _, propPair := range propsList {
|
||||||
switch propPair[0] {
|
switch propPair[0] {
|
||||||
case "VERSION_NAME":
|
case "VERSION_NAME":
|
||||||
if propPair[1] != newVersion.String() {
|
if propPair[1] != newVersion {
|
||||||
versionUpdated = true
|
versionUpdated = true
|
||||||
propPair[1] = newVersion.String()
|
propPair[1] = newVersion
|
||||||
log.Info("updated version to ", newVersion.String())
|
log.Info("updated version to ", newVersion)
|
||||||
}
|
}
|
||||||
case "GO_VERSION":
|
case "GO_VERSION":
|
||||||
if propPair[1] != runtime.Version() {
|
if propPair[1] != runtime.Version() {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -13,9 +14,22 @@ import (
|
|||||||
"howett.net/plist"
|
"howett.net/plist"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var flagRunInCI bool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.BoolVar(&flagRunInCI, "ci", false, "Run in CI")
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
newVersion := common.Must1(build_shared.ReadTagVersion())
|
newVersion := common.Must1(build_shared.ReadTagVersion())
|
||||||
applePath, err := filepath.Abs("../sing-box-for-apple")
|
var applePath string
|
||||||
|
if flagRunInCI {
|
||||||
|
applePath = "clients/apple"
|
||||||
|
} else {
|
||||||
|
applePath = "../sing-box-for-apple"
|
||||||
|
}
|
||||||
|
applePath, err := filepath.Abs(applePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func createPreStartedClient() (*box.Box, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
instance, err := box.New(box.Options{Options: options})
|
instance, err := box.New(box.Options{Context: globalCtx, Options: options})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "create service")
|
return nil, E.Cause(err, "create service")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,25 +88,31 @@ func NewDefault(networkManager adapter.NetworkManager, options option.DialerOpti
|
|||||||
}
|
}
|
||||||
if networkManager != nil && options.BindInterface == "" && options.Inet4BindAddress == nil && options.Inet6BindAddress == nil {
|
if networkManager != nil && options.BindInterface == "" && options.Inet4BindAddress == nil && options.Inet6BindAddress == nil {
|
||||||
defaultOptions := networkManager.DefaultOptions()
|
defaultOptions := networkManager.DefaultOptions()
|
||||||
if defaultOptions.BindInterface != "" {
|
if options.BindInterface == "" {
|
||||||
bindFunc := control.BindToInterface(networkManager.InterfaceFinder(), defaultOptions.BindInterface, -1)
|
if defaultOptions.BindInterface != "" {
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
bindFunc := control.BindToInterface(networkManager.InterfaceFinder(), defaultOptions.BindInterface, -1)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
|
||||||
} else if networkManager.AutoDetectInterface() {
|
|
||||||
if defaultOptions.NetworkStrategy != C.NetworkStrategyDefault && C.NetworkStrategy(options.NetworkStrategy) == C.NetworkStrategyDefault {
|
|
||||||
networkStrategy = defaultOptions.NetworkStrategy
|
|
||||||
networkType = defaultOptions.NetworkType
|
|
||||||
fallbackNetworkType = defaultOptions.FallbackNetworkType
|
|
||||||
networkFallbackDelay = defaultOptions.FallbackDelay
|
|
||||||
bindFunc := networkManager.ProtectFunc()
|
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
|
||||||
} else {
|
|
||||||
bindFunc := networkManager.AutoDetectInterfaceFunc()
|
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
|
} else if networkManager.AutoDetectInterface() {
|
||||||
|
if defaultOptions.NetworkStrategy != C.NetworkStrategyDefault && C.NetworkStrategy(options.NetworkStrategy) == C.NetworkStrategyDefault {
|
||||||
|
networkStrategy = defaultOptions.NetworkStrategy
|
||||||
|
networkType = defaultOptions.NetworkType
|
||||||
|
fallbackNetworkType = defaultOptions.FallbackNetworkType
|
||||||
|
networkFallbackDelay = defaultOptions.FallbackDelay
|
||||||
|
bindFunc := networkManager.ProtectFunc()
|
||||||
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
|
} else {
|
||||||
|
bindFunc := networkManager.AutoDetectInterfaceFunc()
|
||||||
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if options.RoutingMark == 0 && defaultOptions.RoutingMark != 0 {
|
||||||
|
dialer.Control = control.Append(dialer.Control, control.RoutingMark(defaultOptions.RoutingMark))
|
||||||
|
listener.Control = control.Append(listener.Control, control.RoutingMark(defaultOptions.RoutingMark))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if options.ReuseAddr {
|
if options.ReuseAddr {
|
||||||
listener.Control = control.Append(listener.Control, control.ReuseAddr())
|
listener.Control = control.Append(listener.Control, control.ReuseAddr())
|
||||||
@@ -279,7 +285,7 @@ func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destina
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
|
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
|
||||||
return trackPacketConn(d.udpListener.ListenPacket(context.Background(), network, address))
|
return d.udpListener.ListenPacket(context.Background(), network, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func trackConn(conn net.Conn, err error) (net.Conn, error) {
|
func trackConn(conn net.Conn, err error) (net.Conn, error) {
|
||||||
|
|||||||
@@ -149,9 +149,6 @@ func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listene
|
|||||||
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
||||||
return nil, E.New("no available network interface")
|
return nil, E.New("no available network interface")
|
||||||
}
|
}
|
||||||
if fallbackDelay == 0 {
|
|
||||||
fallbackDelay = N.DefaultFallbackDelay
|
|
||||||
}
|
|
||||||
var errors []error
|
var errors []error
|
||||||
for _, primaryInterface := range primaryInterfaces {
|
for _, primaryInterface := range primaryInterfaces {
|
||||||
perNetListener := listener
|
perNetListener := listener
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ func NewRouterWithOptions(router adapter.ConnectionRouterEx, logger logger.Conte
|
|||||||
NewStreamContext: func(ctx context.Context, conn net.Conn) context.Context {
|
NewStreamContext: func(ctx context.Context, conn net.Conn) context.Context {
|
||||||
return log.ContextWithNewID(ctx)
|
return log.ContextWithNewID(ctx)
|
||||||
},
|
},
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
Handler: adapter.NewRouteContextHandler(router, logger),
|
HandlerEx: adapter.NewRouteContextHandlerEx(router),
|
||||||
Padding: options.Padding,
|
Padding: options.Padding,
|
||||||
Brutal: brutalOptions,
|
Brutal: brutalOptions,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -52,6 +52,7 @@ func NewRouterWithOptions(router adapter.ConnectionRouterEx, logger logger.Conte
|
|||||||
return &Router{router, service}, nil
|
return &Router{router, service}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use RouteConnectionEx instead.
|
||||||
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
if metadata.Destination == mux.Destination {
|
if metadata.Destination == mux.Destination {
|
||||||
// TODO: check if WithContext is necessary
|
// TODO: check if WithContext is necessary
|
||||||
@@ -61,6 +62,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use RoutePacketConnectionEx instead.
|
||||||
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
return r.router.RoutePacketConnection(ctx, conn, metadata)
|
return r.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,14 +30,15 @@ func NewClient(ctx context.Context, serverAddress string, options option.Outboun
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if options.ECH != nil && options.ECH.Enabled {
|
if options.ECH != nil && options.ECH.Enabled {
|
||||||
return NewECHClient(ctx, serverAddress, options)
|
if options.ECH.PQSignatureSchemesEnabled || options.ECH.DynamicRecordSizingDisabled {
|
||||||
|
return NewECHClient(ctx, serverAddress, options)
|
||||||
|
}
|
||||||
} else if options.Reality != nil && options.Reality.Enabled {
|
} else if options.Reality != nil && options.Reality.Enabled {
|
||||||
return NewRealityClient(ctx, serverAddress, options)
|
return NewRealityClient(ctx, serverAddress, options)
|
||||||
} else if options.UTLS != nil && options.UTLS.Enabled {
|
} else if options.UTLS != nil && options.UTLS.Enabled {
|
||||||
return NewUTLSClient(ctx, serverAddress, options)
|
return NewUTLSClient(ctx, serverAddress, options)
|
||||||
} else {
|
|
||||||
return NewSTDClient(ctx, serverAddress, options)
|
|
||||||
}
|
}
|
||||||
|
return NewSTDClient(ctx, serverAddress, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
|
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
|
||||||
cftls "github.com/sagernet/cloudflare-tls"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
|
||||||
"github.com/cloudflare/circl/hpke"
|
"github.com/cloudflare/circl/hpke"
|
||||||
@@ -59,7 +58,6 @@ func ECHKeygenDefault(serverName string, pqSignatureSchemesEnabled bool) (config
|
|||||||
|
|
||||||
type echKeyConfigPair struct {
|
type echKeyConfigPair struct {
|
||||||
id uint8
|
id uint8
|
||||||
key cftls.EXP_ECHKey
|
|
||||||
rawKey []byte
|
rawKey []byte
|
||||||
conf myECHKeyConfig
|
conf myECHKeyConfig
|
||||||
rawConf []byte
|
rawConf []byte
|
||||||
@@ -153,14 +151,13 @@ func echKeygen(version uint16, serverName string, conf []myECHKeyConfig, suite [
|
|||||||
sk = be.AppendUint16(sk, uint16(len(b)))
|
sk = be.AppendUint16(sk, uint16(len(b)))
|
||||||
sk = append(sk, b...)
|
sk = append(sk, b...)
|
||||||
|
|
||||||
cfECHKeys, err := cftls.EXP_UnmarshalECHKeys(sk)
|
cfECHKeys, err := UnmarshalECHKeys(sk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "bug: can't parse generated ECH server key")
|
return nil, E.Cause(err, "bug: can't parse generated ECH server key")
|
||||||
}
|
}
|
||||||
if len(cfECHKeys) != 1 {
|
if len(cfECHKeys) != 1 {
|
||||||
return nil, E.New("bug: unexpected server key count")
|
return nil, E.New("bug: unexpected server key count")
|
||||||
}
|
}
|
||||||
pair.key = cfECHKeys[0]
|
|
||||||
pair.rawKey = sk
|
pair.rawKey = sk
|
||||||
|
|
||||||
pairs = append(pairs, pair)
|
pairs = append(pairs, pair)
|
||||||
|
|||||||
@@ -17,12 +17,13 @@ func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLS
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if options.ECH != nil && options.ECH.Enabled {
|
if options.ECH != nil && options.ECH.Enabled {
|
||||||
return NewECHServer(ctx, logger, options)
|
if options.ECH.PQSignatureSchemesEnabled || options.ECH.DynamicRecordSizingDisabled {
|
||||||
|
return NewECHServer(ctx, logger, options)
|
||||||
|
}
|
||||||
} else if options.Reality != nil && options.Reality.Enabled {
|
} else if options.Reality != nil && options.Reality.Enabled {
|
||||||
return NewRealityServer(ctx, logger, options)
|
return NewRealityServer(ctx, logger, options)
|
||||||
} else {
|
|
||||||
return NewSTDServer(ctx, logger, options)
|
|
||||||
}
|
}
|
||||||
|
return NewSTDServer(ctx, logger, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) {
|
func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) {
|
||||||
|
|||||||
@@ -4,16 +4,25 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-dns"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
|
aTLS "github.com/sagernet/sing/common/tls"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
|
|
||||||
|
mDNS "github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ ConfigCompat = (*STDClientConfig)(nil)
|
||||||
|
|
||||||
type STDClientConfig struct {
|
type STDClientConfig struct {
|
||||||
config *tls.Config
|
config *tls.Config
|
||||||
}
|
}
|
||||||
@@ -46,6 +55,63 @@ func (s *STDClientConfig) Clone() Config {
|
|||||||
return &STDClientConfig{s.config.Clone()}
|
return &STDClientConfig{s.config.Clone()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type STDECHClientConfig struct {
|
||||||
|
STDClientConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *STDClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
|
||||||
|
if len(s.config.EncryptedClientHelloConfigList) == 0 {
|
||||||
|
message := &mDNS.Msg{
|
||||||
|
MsgHdr: mDNS.MsgHdr{
|
||||||
|
RecursionDesired: true,
|
||||||
|
},
|
||||||
|
Question: []mDNS.Question{
|
||||||
|
{
|
||||||
|
Name: mDNS.Fqdn(s.config.ServerName),
|
||||||
|
Qtype: mDNS.TypeHTTPS,
|
||||||
|
Qclass: mDNS.ClassINET,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
dnsRouter := service.FromContext[adapter.Router](ctx)
|
||||||
|
response, err := dnsRouter.Exchange(ctx, message)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "fetch ECH config list")
|
||||||
|
}
|
||||||
|
if response.Rcode != mDNS.RcodeSuccess {
|
||||||
|
return nil, E.Cause(dns.RCodeError(response.Rcode), "fetch ECH config list")
|
||||||
|
}
|
||||||
|
for _, rr := range response.Answer {
|
||||||
|
switch resource := rr.(type) {
|
||||||
|
case *mDNS.HTTPS:
|
||||||
|
for _, value := range resource.Value {
|
||||||
|
if value.Key().String() == "ech" {
|
||||||
|
echConfigList, err := base64.StdEncoding.DecodeString(value.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "decode ECH config")
|
||||||
|
}
|
||||||
|
s.config.EncryptedClientHelloConfigList = echConfigList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, E.New("no ECH config found in DNS records")
|
||||||
|
}
|
||||||
|
tlsConn, err := s.Client(conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = tlsConn.HandshakeContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tlsConn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *STDECHClientConfig) Clone() Config {
|
||||||
|
return &STDECHClientConfig{STDClientConfig{s.config.Clone()}}
|
||||||
|
}
|
||||||
|
|
||||||
func NewSTDClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
func NewSTDClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||||
var serverName string
|
var serverName string
|
||||||
if options.ServerName != "" {
|
if options.ServerName != "" {
|
||||||
@@ -128,5 +194,21 @@ func NewSTDClient(ctx context.Context, serverAddress string, options option.Outb
|
|||||||
}
|
}
|
||||||
tlsConfig.RootCAs = certPool
|
tlsConfig.RootCAs = certPool
|
||||||
}
|
}
|
||||||
|
if options.ECH != nil && options.ECH.Enabled {
|
||||||
|
var echConfig []byte
|
||||||
|
if len(options.ECH.Config) > 0 {
|
||||||
|
echConfig = []byte(strings.Join(options.ECH.Config, "\n"))
|
||||||
|
} else if options.ECH.ConfigPath != "" {
|
||||||
|
content, err := os.ReadFile(options.ECH.ConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "read ECH config")
|
||||||
|
}
|
||||||
|
echConfig = content
|
||||||
|
}
|
||||||
|
if echConfig != nil {
|
||||||
|
tlsConfig.EncryptedClientHelloConfigList = echConfig
|
||||||
|
}
|
||||||
|
return &STDECHClientConfig{STDClientConfig{&tlsConfig}}, nil
|
||||||
|
}
|
||||||
return &STDClientConfig{&tlsConfig}, nil
|
return &STDClientConfig{&tlsConfig}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package tls
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/pem"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -14,6 +15,8 @@ import (
|
|||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/cryptobyte"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errInsecureUnused = E.New("tls: insecure unused")
|
var errInsecureUnused = E.New("tls: insecure unused")
|
||||||
@@ -238,6 +241,31 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
|||||||
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if options.ECH != nil && options.ECH.Enabled {
|
||||||
|
var echKey []byte
|
||||||
|
if len(options.ECH.Key) > 0 {
|
||||||
|
echKey = []byte(strings.Join(options.ECH.Key, "\n"))
|
||||||
|
} else if options.ECH.KeyPath != "" {
|
||||||
|
content, err := os.ReadFile(options.ECH.KeyPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "read ECH key")
|
||||||
|
}
|
||||||
|
echKey = content
|
||||||
|
} else {
|
||||||
|
return nil, E.New("missing ECH key")
|
||||||
|
}
|
||||||
|
|
||||||
|
block, rest := pem.Decode(echKey)
|
||||||
|
if block == nil || block.Type != "ECH KEYS" || len(rest) > 0 {
|
||||||
|
return nil, E.New("invalid ECH keys pem")
|
||||||
|
}
|
||||||
|
|
||||||
|
echKeys, err := UnmarshalECHKeys(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "parse ECH keys")
|
||||||
|
}
|
||||||
|
tlsConfig.EncryptedClientHelloKeys = echKeys
|
||||||
|
}
|
||||||
return &STDServerConfig{
|
return &STDServerConfig{
|
||||||
config: tlsConfig,
|
config: tlsConfig,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@@ -248,3 +276,22 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
|||||||
keyPath: options.KeyPath,
|
keyPath: options.KeyPath,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UnmarshalECHKeys(raw []byte) ([]tls.EncryptedClientHelloKey, error) {
|
||||||
|
var keys []tls.EncryptedClientHelloKey
|
||||||
|
rawString := cryptobyte.String(raw)
|
||||||
|
for !rawString.Empty() {
|
||||||
|
var key tls.EncryptedClientHelloKey
|
||||||
|
if !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.PrivateKey)) {
|
||||||
|
return nil, E.New("error parsing private key")
|
||||||
|
}
|
||||||
|
if !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.Config)) {
|
||||||
|
return nil, E.New("error parsing config")
|
||||||
|
}
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return nil, E.New("empty ECH keys")
|
||||||
|
}
|
||||||
|
return keys, nil
|
||||||
|
}
|
||||||
|
|||||||
8
constant/cgo_android_fix.go
Normal file
8
constant/cgo_android_fix.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
//go:build android && debug
|
||||||
|
|
||||||
|
package constant
|
||||||
|
|
||||||
|
// TODO: remove after fixed
|
||||||
|
// https://github.com/golang/go/issues/68760
|
||||||
|
|
||||||
|
const FixAndroidStack = true
|
||||||
5
constant/cgo_android_fix_stub.go
Normal file
5
constant/cgo_android_fix_stub.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
//go:build !(android && debug)
|
||||||
|
|
||||||
|
package constant
|
||||||
|
|
||||||
|
const FixAndroidStack = false
|
||||||
7
constant/hysteria2.go
Normal file
7
constant/hysteria2.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package constant
|
||||||
|
|
||||||
|
const (
|
||||||
|
Hysterai2MasqueradeTypeFile = "file"
|
||||||
|
Hysterai2MasqueradeTypeProxy = "proxy"
|
||||||
|
Hysterai2MasqueradeTypeString = "string"
|
||||||
|
)
|
||||||
@@ -10,6 +10,7 @@ const (
|
|||||||
ProtocolDTLS = "dtls"
|
ProtocolDTLS = "dtls"
|
||||||
ProtocolSSH = "ssh"
|
ProtocolSSH = "ssh"
|
||||||
ProtocolRDP = "rdp"
|
ProtocolRDP = "rdp"
|
||||||
|
ProtocolNTP = "ntp"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ const (
|
|||||||
TCPTimeout = 15 * time.Second
|
TCPTimeout = 15 * time.Second
|
||||||
ReadPayloadTimeout = 300 * time.Millisecond
|
ReadPayloadTimeout = 300 * time.Millisecond
|
||||||
DNSTimeout = 10 * time.Second
|
DNSTimeout = 10 * time.Second
|
||||||
QUICTimeout = 30 * time.Second
|
|
||||||
STUNTimeout = 15 * time.Second
|
|
||||||
UDPTimeout = 5 * time.Minute
|
UDPTimeout = 5 * time.Minute
|
||||||
DefaultURLTestInterval = 3 * time.Minute
|
DefaultURLTestInterval = 3 * time.Minute
|
||||||
DefaultURLTestIdleTimeout = 30 * time.Minute
|
DefaultURLTestIdleTimeout = 30 * time.Minute
|
||||||
@@ -19,3 +17,18 @@ const (
|
|||||||
FatalStopTimeout = 10 * time.Second
|
FatalStopTimeout = 10 * time.Second
|
||||||
FakeIPMetadataSaveInterval = 10 * time.Second
|
FakeIPMetadataSaveInterval = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var PortProtocols = map[uint16]string{
|
||||||
|
53: ProtocolDNS,
|
||||||
|
123: ProtocolNTP,
|
||||||
|
3478: ProtocolSTUN,
|
||||||
|
443: ProtocolQUIC,
|
||||||
|
}
|
||||||
|
|
||||||
|
var ProtocolTimeouts = map[string]time.Duration{
|
||||||
|
ProtocolDNS: 10 * time.Second,
|
||||||
|
ProtocolNTP: 10 * time.Second,
|
||||||
|
ProtocolSTUN: 10 * time.Second,
|
||||||
|
ProtocolQUIC: 30 * time.Second,
|
||||||
|
ProtocolDTLS: 30 * time.Second,
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,50 @@
|
|||||||
icon: material/alert-decagram
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
|
#### 1.11.0-beta.9
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.11.0-beta.3
|
||||||
|
|
||||||
|
* Add more masquerade options for hysteria2 **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
See [Hysteria2](/configuration/inbound/hysteria2/#masquerade).
|
||||||
|
|
||||||
|
### 1.10.3
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.11.0-alpha.25
|
||||||
|
|
||||||
|
* Update quic-go to v0.48.2
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.11.0-alpha.22
|
||||||
|
|
||||||
|
* Add UDP timeout route option **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
See [Rule Action](/configuration/route/rule_action/#udp_timeout).
|
||||||
|
|
||||||
|
#### 1.11.0-alpha.20
|
||||||
|
|
||||||
|
* Add UDP GSO support for WireGuard
|
||||||
|
* Make GSO adaptive **1**
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
For WireGuard outbound and endpoint, GSO will be automatically enabled when available,
|
||||||
|
see [WireGuard Outbound](/configuration/outbound/wireguard/#gso).
|
||||||
|
|
||||||
|
For TUN, GSO has been removed,
|
||||||
|
see [Deprecated](/deprecated/#gso-option-in-tun).
|
||||||
|
|
||||||
#### 1.11.0-alpha.19
|
#### 1.11.0-alpha.19
|
||||||
|
|
||||||
* Upgrade WireGuard outbound to endpoint **1**
|
* Upgrade WireGuard outbound to endpoint **1**
|
||||||
|
|||||||
@@ -379,7 +379,7 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
|||||||
|
|
||||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||||
|
|
||||||
`rule_set_ipcidr_match_source` 已重命名为 `rule_set_ip_cidr_match_source` 且将在 sing-box 1.11.0 移除。
|
`rule_set_ipcidr_match_source` 已重命名为 `rule_set_ip_cidr_match_source` 且将在 sing-box 1.11.0 中被移除。
|
||||||
|
|
||||||
使规则集中的 `ip_cidr` 规则匹配源 IP。
|
使规则集中的 `ip_cidr` 规则匹配源 IP。
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ icon: material/new-box
|
|||||||
"system": false,
|
"system": false,
|
||||||
"name": "",
|
"name": "",
|
||||||
"mtu": 1408,
|
"mtu": 1408,
|
||||||
"gso": false,
|
|
||||||
"address": [],
|
"address": [],
|
||||||
"private_key": "",
|
"private_key": "",
|
||||||
"listen_port": 10000,
|
"listen_port": 10000,
|
||||||
@@ -36,6 +35,10 @@ icon: material/new-box
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
You can ignore the JSON Array [] tag when the content is only one item
|
||||||
|
|
||||||
### Fields
|
### Fields
|
||||||
|
|
||||||
#### system
|
#### system
|
||||||
@@ -54,14 +57,6 @@ WireGuard MTU.
|
|||||||
|
|
||||||
`1408` will be used by default.
|
`1408` will be used by default.
|
||||||
|
|
||||||
#### gso
|
|
||||||
|
|
||||||
!!! quote ""
|
|
||||||
|
|
||||||
Only supported on Linux.
|
|
||||||
|
|
||||||
Try to enable generic segmentation offload.
|
|
||||||
|
|
||||||
#### address
|
#### address
|
||||||
|
|
||||||
==Required==
|
==Required==
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ icon: material/new-box
|
|||||||
"system": false,
|
"system": false,
|
||||||
"name": "",
|
"name": "",
|
||||||
"mtu": 1408,
|
"mtu": 1408,
|
||||||
"gso": false,
|
|
||||||
"address": [],
|
"address": [],
|
||||||
"private_key": "",
|
"private_key": "",
|
||||||
"listen_port": 10000,
|
"listen_port": 10000,
|
||||||
@@ -36,6 +35,10 @@ icon: material/new-box
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
当内容只有一项时,可以忽略 JSON 数组 [] 标签
|
||||||
|
|
||||||
### 字段
|
### 字段
|
||||||
|
|
||||||
#### system_interface
|
#### system_interface
|
||||||
@@ -54,14 +57,6 @@ WireGuard MTU。
|
|||||||
|
|
||||||
默认使用 1408。
|
默认使用 1408。
|
||||||
|
|
||||||
#### gso
|
|
||||||
|
|
||||||
!!! quote ""
|
|
||||||
|
|
||||||
仅支持 Linux。
|
|
||||||
|
|
||||||
尝试启用通用分段卸载。
|
|
||||||
|
|
||||||
#### address
|
#### address
|
||||||
|
|
||||||
==必填==
|
==必填==
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
|
---
|
||||||
|
icon: material/alert-decagram
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! quote "Changes in sing-box 1.11.0"
|
||||||
|
|
||||||
|
:material-alert: [masquerade](#masquerade)
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "hysteria2",
|
"type": "hysteria2",
|
||||||
"tag": "hy2-in",
|
"tag": "hy2-in",
|
||||||
...
|
|
||||||
// Listen Fields
|
... // Listen Fields
|
||||||
|
|
||||||
"up_mbps": 100,
|
"up_mbps": 100,
|
||||||
"down_mbps": 100,
|
"down_mbps": 100,
|
||||||
@@ -21,7 +29,7 @@
|
|||||||
],
|
],
|
||||||
"ignore_client_bandwidth": false,
|
"ignore_client_bandwidth": false,
|
||||||
"tls": {},
|
"tls": {},
|
||||||
"masquerade": "",
|
"masquerade": "", // or {}
|
||||||
"brutal_debug": false
|
"brutal_debug": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -79,14 +87,54 @@ TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
|||||||
|
|
||||||
#### masquerade
|
#### masquerade
|
||||||
|
|
||||||
HTTP3 server behavior when authentication fails.
|
HTTP3 server behavior (URL string configuration) when authentication fails.
|
||||||
|
|
||||||
| Scheme | Example | Description |
|
| Scheme | Example | Description |
|
||||||
|--------------|-------------------------|--------------------|
|
|--------------|-------------------------|--------------------|
|
||||||
| `file` | `file:///var/www` | As a file server |
|
| `file` | `file:///var/www` | As a file server |
|
||||||
| `http/https` | `http://127.0.0.1:8080` | As a reverse proxy |
|
| `http/https` | `http://127.0.0.1:8080` | As a reverse proxy |
|
||||||
|
|
||||||
A 404 page will be returned if empty.
|
Conflict with `masquerade.type`.
|
||||||
|
|
||||||
|
A 404 page will be returned if masquerade is not configured.
|
||||||
|
|
||||||
|
#### masquerade.type
|
||||||
|
|
||||||
|
HTTP3 server behavior (Object configuration) when authentication fails.
|
||||||
|
|
||||||
|
| Type | Description | Fields |
|
||||||
|
|----------|-----------------------------|-------------------------------------|
|
||||||
|
| `file` | As a file server | `directory` |
|
||||||
|
| `proxy` | As a reverse proxy | `url`, `rewrite_host` |
|
||||||
|
| `string` | Reply with a fixed response | `status_code`, `headers`, `content` |
|
||||||
|
|
||||||
|
Conflict with `masquerade`.
|
||||||
|
|
||||||
|
A 404 page will be returned if masquerade is not configured.
|
||||||
|
|
||||||
|
#### masquerade.directory
|
||||||
|
|
||||||
|
File server root directory.
|
||||||
|
|
||||||
|
#### masquerade.url
|
||||||
|
|
||||||
|
Reverse proxy target URL.
|
||||||
|
|
||||||
|
#### masquerade.rewrite_host
|
||||||
|
|
||||||
|
Rewrite the `Host` header to the target URL.
|
||||||
|
|
||||||
|
#### masquerade.status_code
|
||||||
|
|
||||||
|
Fixed response status code.
|
||||||
|
|
||||||
|
#### masquerade.headers
|
||||||
|
|
||||||
|
Fixed response headers.
|
||||||
|
|
||||||
|
#### masquerade.content
|
||||||
|
|
||||||
|
Fixed response content.
|
||||||
|
|
||||||
#### brutal_debug
|
#### brutal_debug
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
|
---
|
||||||
|
icon: material/alert-decagram
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! quote "sing-box 1.11.0 中的更改"
|
||||||
|
|
||||||
|
:material-alert: [masquerade](#masquerade)
|
||||||
|
|
||||||
### 结构
|
### 结构
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "hysteria2",
|
"type": "hysteria2",
|
||||||
"tag": "hy2-in",
|
"tag": "hy2-in",
|
||||||
...
|
|
||||||
// 监听字段
|
... // 监听字段
|
||||||
|
|
||||||
"up_mbps": 100,
|
"up_mbps": 100,
|
||||||
"down_mbps": 100,
|
"down_mbps": 100,
|
||||||
@@ -21,7 +29,7 @@
|
|||||||
],
|
],
|
||||||
"ignore_client_bandwidth": false,
|
"ignore_client_bandwidth": false,
|
||||||
"tls": {},
|
"tls": {},
|
||||||
"masquerade": "",
|
"masquerade": "", // 或 {}
|
||||||
"brutal_debug": false
|
"brutal_debug": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -76,14 +84,54 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
|||||||
|
|
||||||
#### masquerade
|
#### masquerade
|
||||||
|
|
||||||
HTTP3 服务器认证失败时的行为。
|
HTTP3 服务器认证失败时的行为 (URL 字符串配置)。
|
||||||
|
|
||||||
| Scheme | 示例 | 描述 |
|
| Scheme | 示例 | 描述 |
|
||||||
|--------------|-------------------------|---------|
|
|--------------|-------------------------|---------|
|
||||||
| `file` | `file:///var/www` | 作为文件服务器 |
|
| `file` | `file:///var/www` | 作为文件服务器 |
|
||||||
| `http/https` | `http://127.0.0.1:8080` | 作为反向代理 |
|
| `http/https` | `http://127.0.0.1:8080` | 作为反向代理 |
|
||||||
|
|
||||||
如果为空,则返回 404 页。
|
如果 masquerade 未配置,则返回 404 页。
|
||||||
|
|
||||||
|
与 `masquerade.type` 冲突。
|
||||||
|
|
||||||
|
#### masquerade.type
|
||||||
|
|
||||||
|
HTTP3 服务器认证失败时的行为 (对象配置)。
|
||||||
|
|
||||||
|
| Type | 描述 | 字段 |
|
||||||
|
|----------|---------|-------------------------------------|
|
||||||
|
| `file` | 作为文件服务器 | `directory` |
|
||||||
|
| `proxy` | 作为反向代理 | `url`, `rewrite_host` |
|
||||||
|
| `string` | 返回固定响应 | `status_code`, `headers`, `content` |
|
||||||
|
|
||||||
|
如果 masquerade 未配置,则返回 404 页。
|
||||||
|
|
||||||
|
与 `masquerade` 冲突。
|
||||||
|
|
||||||
|
#### masquerade.directory
|
||||||
|
|
||||||
|
文件服务器根目录。
|
||||||
|
|
||||||
|
#### masquerade.url
|
||||||
|
|
||||||
|
反向代理目标 URL。
|
||||||
|
|
||||||
|
#### masquerade.rewrite_host
|
||||||
|
|
||||||
|
重写请求头中的 Host 字段到目标 URL。
|
||||||
|
|
||||||
|
#### masquerade.status_code
|
||||||
|
|
||||||
|
固定响应状态码。
|
||||||
|
|
||||||
|
#### masquerade.headers
|
||||||
|
|
||||||
|
固定响应头。
|
||||||
|
|
||||||
|
#### masquerade.content
|
||||||
|
|
||||||
|
固定响应内容。
|
||||||
|
|
||||||
#### brutal_debug
|
#### brutal_debug
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
---
|
---
|
||||||
icon: material/new-box
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
|
!!! quote "Changes in sing-box 1.11.0"
|
||||||
|
|
||||||
|
:material-delete-alert: [gso](#gso)
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.10.0"
|
!!! quote "Changes in sing-box 1.10.0"
|
||||||
|
|
||||||
:material-plus: [address](#address)
|
:material-plus: [address](#address)
|
||||||
@@ -46,16 +50,7 @@ icon: material/new-box
|
|||||||
"172.18.0.1/30",
|
"172.18.0.1/30",
|
||||||
"fdfe:dcba:9876::1/126"
|
"fdfe:dcba:9876::1/126"
|
||||||
],
|
],
|
||||||
// deprecated
|
|
||||||
"inet4_address": [
|
|
||||||
"172.19.0.1/30"
|
|
||||||
],
|
|
||||||
// deprecated
|
|
||||||
"inet6_address": [
|
|
||||||
"fdfe:dcba:9876::1/126"
|
|
||||||
],
|
|
||||||
"mtu": 9000,
|
"mtu": 9000,
|
||||||
"gso": false,
|
|
||||||
"auto_route": true,
|
"auto_route": true,
|
||||||
"iproute2_table_index": 2022,
|
"iproute2_table_index": 2022,
|
||||||
"iproute2_rule_index": 9000,
|
"iproute2_rule_index": 9000,
|
||||||
@@ -69,28 +64,11 @@ icon: material/new-box
|
|||||||
"::/1",
|
"::/1",
|
||||||
"8000::/1"
|
"8000::/1"
|
||||||
],
|
],
|
||||||
// deprecated
|
|
||||||
"inet4_route_address": [
|
|
||||||
"0.0.0.0/1",
|
|
||||||
"128.0.0.0/1"
|
|
||||||
],
|
|
||||||
// deprecated
|
|
||||||
"inet6_route_address": [
|
|
||||||
"::/1",
|
|
||||||
"8000::/1"
|
|
||||||
],
|
|
||||||
"route_exclude_address": [
|
"route_exclude_address": [
|
||||||
"192.168.0.0/16",
|
"192.168.0.0/16",
|
||||||
"fc00::/7"
|
"fc00::/7"
|
||||||
],
|
],
|
||||||
// deprecated
|
|
||||||
"inet4_route_exclude_address": [
|
|
||||||
"192.168.0.0/16"
|
|
||||||
],
|
|
||||||
// deprecated
|
|
||||||
"inet6_route_exclude_address": [
|
|
||||||
"fc00::/7"
|
|
||||||
],
|
|
||||||
"route_address_set": [
|
"route_address_set": [
|
||||||
"geoip-cloudflare"
|
"geoip-cloudflare"
|
||||||
],
|
],
|
||||||
@@ -137,8 +115,31 @@ icon: material/new-box
|
|||||||
"match_domain": []
|
"match_domain": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
...
|
|
||||||
// Listen Fields
|
// Deprecated
|
||||||
|
"gso": false,
|
||||||
|
"inet4_address": [
|
||||||
|
"172.19.0.1/30"
|
||||||
|
],
|
||||||
|
"inet6_address": [
|
||||||
|
"fdfe:dcba:9876::1/126"
|
||||||
|
],
|
||||||
|
"inet4_route_address": [
|
||||||
|
"0.0.0.0/1",
|
||||||
|
"128.0.0.0/1"
|
||||||
|
],
|
||||||
|
"inet6_route_address": [
|
||||||
|
"::/1",
|
||||||
|
"8000::/1"
|
||||||
|
],
|
||||||
|
"inet4_route_exclude_address": [
|
||||||
|
"192.168.0.0/16"
|
||||||
|
],
|
||||||
|
"inet6_route_exclude_address": [
|
||||||
|
"fc00::/7"
|
||||||
|
],
|
||||||
|
|
||||||
|
... // Listen Fields
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -166,7 +167,7 @@ IPv4 and IPv6 prefix for the tun interface.
|
|||||||
|
|
||||||
!!! failure "Deprecated in sing-box 1.10.0"
|
!!! failure "Deprecated in sing-box 1.10.0"
|
||||||
|
|
||||||
`inet4_address` is merged to `address` and will be removed in sing-box 1.11.0.
|
`inet4_address` is merged to `address` and will be removed in sing-box 1.12.0.
|
||||||
|
|
||||||
IPv4 prefix for the tun interface.
|
IPv4 prefix for the tun interface.
|
||||||
|
|
||||||
@@ -174,7 +175,7 @@ IPv4 prefix for the tun interface.
|
|||||||
|
|
||||||
!!! failure "Deprecated in sing-box 1.10.0"
|
!!! failure "Deprecated in sing-box 1.10.0"
|
||||||
|
|
||||||
`inet6_address` is merged to `address` and will be removed in sing-box 1.11.0.
|
`inet6_address` is merged to `address` and will be removed in sing-box 1.12.0.
|
||||||
|
|
||||||
IPv6 prefix for the tun interface.
|
IPv6 prefix for the tun interface.
|
||||||
|
|
||||||
@@ -184,6 +185,10 @@ The maximum transmission unit.
|
|||||||
|
|
||||||
#### gso
|
#### gso
|
||||||
|
|
||||||
|
!!! failure "Deprecated in sing-box 1.11.0"
|
||||||
|
|
||||||
|
GSO has no advantages for transparent proxy scenarios, is deprecated and no longer works, and will be removed in sing-box 1.12.0.
|
||||||
|
|
||||||
!!! question "Since sing-box 1.8.0"
|
!!! question "Since sing-box 1.8.0"
|
||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
@@ -284,7 +289,7 @@ Use custom routes instead of default when `auto_route` is enabled.
|
|||||||
|
|
||||||
!!! failure "Deprecated in sing-box 1.10.0"
|
!!! failure "Deprecated in sing-box 1.10.0"
|
||||||
|
|
||||||
`inet4_route_address` is deprecated and will be removed in sing-box 1.11.0, please use [route_address](#route_address)
|
`inet4_route_address` is deprecated and will be removed in sing-box 1.12.0, please use [route_address](#route_address)
|
||||||
instead.
|
instead.
|
||||||
|
|
||||||
Use custom routes instead of default when `auto_route` is enabled.
|
Use custom routes instead of default when `auto_route` is enabled.
|
||||||
@@ -293,7 +298,7 @@ Use custom routes instead of default when `auto_route` is enabled.
|
|||||||
|
|
||||||
!!! failure "Deprecated in sing-box 1.10.0"
|
!!! failure "Deprecated in sing-box 1.10.0"
|
||||||
|
|
||||||
`inet6_route_address` is deprecated and will be removed in sing-box 1.11.0, please use [route_address](#route_address)
|
`inet6_route_address` is deprecated and will be removed in sing-box 1.12.0, please use [route_address](#route_address)
|
||||||
instead.
|
instead.
|
||||||
|
|
||||||
Use custom routes instead of default when `auto_route` is enabled.
|
Use custom routes instead of default when `auto_route` is enabled.
|
||||||
@@ -308,7 +313,7 @@ Exclude custom routes when `auto_route` is enabled.
|
|||||||
|
|
||||||
!!! failure "Deprecated in sing-box 1.10.0"
|
!!! failure "Deprecated in sing-box 1.10.0"
|
||||||
|
|
||||||
`inet4_route_exclude_address` is deprecated and will be removed in sing-box 1.11.0, please
|
`inet4_route_exclude_address` is deprecated and will be removed in sing-box 1.12.0, please
|
||||||
use [route_exclude_address](#route_exclude_address) instead.
|
use [route_exclude_address](#route_exclude_address) instead.
|
||||||
|
|
||||||
Exclude custom routes when `auto_route` is enabled.
|
Exclude custom routes when `auto_route` is enabled.
|
||||||
@@ -317,7 +322,7 @@ Exclude custom routes when `auto_route` is enabled.
|
|||||||
|
|
||||||
!!! failure "Deprecated in sing-box 1.10.0"
|
!!! failure "Deprecated in sing-box 1.10.0"
|
||||||
|
|
||||||
`inet6_route_exclude_address` is deprecated and will be removed in sing-box 1.11.0, please
|
`inet6_route_exclude_address` is deprecated and will be removed in sing-box 1.12.0, please
|
||||||
use [route_exclude_address](#route_exclude_address) instead.
|
use [route_exclude_address](#route_exclude_address) instead.
|
||||||
|
|
||||||
Exclude custom routes when `auto_route` is enabled.
|
Exclude custom routes when `auto_route` is enabled.
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
---
|
---
|
||||||
icon: material/new-box
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.10.0"
|
!!! quote "sing-box 1.11.0 中的更改"
|
||||||
|
|
||||||
|
:material-delete-alert: [gso](#gso)
|
||||||
|
|
||||||
|
!!! quote "sing-box 1.10.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [address](#address)
|
:material-plus: [address](#address)
|
||||||
:material-delete-clock: [inet4_address](#inet4_address)
|
:material-delete-clock: [inet4_address](#inet4_address)
|
||||||
@@ -46,16 +50,7 @@ icon: material/new-box
|
|||||||
"172.18.0.1/30",
|
"172.18.0.1/30",
|
||||||
"fdfe:dcba:9876::1/126"
|
"fdfe:dcba:9876::1/126"
|
||||||
],
|
],
|
||||||
// 已弃用
|
|
||||||
"inet4_address": [
|
|
||||||
"172.19.0.1/30"
|
|
||||||
],
|
|
||||||
// 已弃用
|
|
||||||
"inet6_address": [
|
|
||||||
"fdfe:dcba:9876::1/126"
|
|
||||||
],
|
|
||||||
"mtu": 9000,
|
"mtu": 9000,
|
||||||
"gso": false,
|
|
||||||
"auto_route": true,
|
"auto_route": true,
|
||||||
"iproute2_table_index": 2022,
|
"iproute2_table_index": 2022,
|
||||||
"iproute2_rule_index": 9000,
|
"iproute2_rule_index": 9000,
|
||||||
@@ -69,28 +64,11 @@ icon: material/new-box
|
|||||||
"::/1",
|
"::/1",
|
||||||
"8000::/1"
|
"8000::/1"
|
||||||
],
|
],
|
||||||
// 已弃用
|
|
||||||
"inet4_route_address": [
|
|
||||||
"0.0.0.0/1",
|
|
||||||
"128.0.0.0/1"
|
|
||||||
],
|
|
||||||
// 已弃用
|
|
||||||
"inet6_route_address": [
|
|
||||||
"::/1",
|
|
||||||
"8000::/1"
|
|
||||||
],
|
|
||||||
"route_exclude_address": [
|
"route_exclude_address": [
|
||||||
"192.168.0.0/16",
|
"192.168.0.0/16",
|
||||||
"fc00::/7"
|
"fc00::/7"
|
||||||
],
|
],
|
||||||
// 已弃用
|
|
||||||
"inet4_route_exclude_address": [
|
|
||||||
"192.168.0.0/16"
|
|
||||||
],
|
|
||||||
// 已弃用
|
|
||||||
"inet6_route_exclude_address": [
|
|
||||||
"fc00::/7"
|
|
||||||
],
|
|
||||||
"route_address_set": [
|
"route_address_set": [
|
||||||
"geoip-cloudflare"
|
"geoip-cloudflare"
|
||||||
],
|
],
|
||||||
@@ -137,6 +115,29 @@ icon: material/new-box
|
|||||||
"match_domain": []
|
"match_domain": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 已弃用
|
||||||
|
"gso": false,
|
||||||
|
"inet4_address": [
|
||||||
|
"172.19.0.1/30"
|
||||||
|
],
|
||||||
|
"inet6_address": [
|
||||||
|
"fdfe:dcba:9876::1/126"
|
||||||
|
],
|
||||||
|
"inet4_route_address": [
|
||||||
|
"0.0.0.0/1",
|
||||||
|
"128.0.0.0/1"
|
||||||
|
],
|
||||||
|
"inet6_route_address": [
|
||||||
|
"::/1",
|
||||||
|
"8000::/1"
|
||||||
|
],
|
||||||
|
"inet4_route_exclude_address": [
|
||||||
|
"192.168.0.0/16"
|
||||||
|
],
|
||||||
|
"inet6_route_exclude_address": [
|
||||||
|
"fc00::/7"
|
||||||
|
],
|
||||||
|
|
||||||
... // 监听字段
|
... // 监听字段
|
||||||
}
|
}
|
||||||
@@ -168,7 +169,7 @@ tun 接口的 IPv4 和 IPv6 前缀。
|
|||||||
|
|
||||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||||
|
|
||||||
`inet4_address` 已合并到 `address` 且将在 sing-box 1.11.0 移除。
|
`inet4_address` 已合并到 `address` 且将在 sing-box 1.12.0 中被移除。
|
||||||
|
|
||||||
==必填==
|
==必填==
|
||||||
|
|
||||||
@@ -178,7 +179,7 @@ tun 接口的 IPv4 前缀。
|
|||||||
|
|
||||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||||
|
|
||||||
`inet6_address` 已合并到 `address` 且将在 sing-box 1.11.0 移除。
|
`inet6_address` 已合并到 `address` 且将在 sing-box 1.12.0 中被移除。
|
||||||
|
|
||||||
tun 接口的 IPv6 前缀。
|
tun 接口的 IPv6 前缀。
|
||||||
|
|
||||||
@@ -188,6 +189,10 @@ tun 接口的 IPv6 前缀。
|
|||||||
|
|
||||||
#### gso
|
#### gso
|
||||||
|
|
||||||
|
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||||
|
|
||||||
|
GSO 对于透明代理场景没有优势,已废弃和不再生效,且将在 sing-box 1.12.0 中被移除。
|
||||||
|
|
||||||
!!! question "自 sing-box 1.8.0 起"
|
!!! question "自 sing-box 1.8.0 起"
|
||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
@@ -288,7 +293,7 @@ tun 接口的 IPv6 前缀。
|
|||||||
|
|
||||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||||
|
|
||||||
`inet4_route_address` 已合并到 `route_address` 且将在 sing-box 1.11.0 移除。
|
`inet4_route_address` 已合并到 `route_address` 且将在 sing-box 1.12.0 中被移除。
|
||||||
|
|
||||||
启用 `auto_route` 时使用自定义路由而不是默认路由。
|
启用 `auto_route` 时使用自定义路由而不是默认路由。
|
||||||
|
|
||||||
@@ -296,7 +301,7 @@ tun 接口的 IPv6 前缀。
|
|||||||
|
|
||||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||||
|
|
||||||
`inet6_route_address` 已合并到 `route_address` 且将在 sing-box 1.11.0 移除。
|
`inet6_route_address` 已合并到 `route_address` 且将在 sing-box 1.12.0 中被移除。
|
||||||
|
|
||||||
启用 `auto_route` 时使用自定义路由而不是默认路由。
|
启用 `auto_route` 时使用自定义路由而不是默认路由。
|
||||||
|
|
||||||
@@ -310,7 +315,7 @@ tun 接口的 IPv6 前缀。
|
|||||||
|
|
||||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||||
|
|
||||||
`inet4_route_exclude_address` 已合并到 `route_exclude_address` 且将在 sing-box 1.11.0 移除。
|
`inet4_route_exclude_address` 已合并到 `route_exclude_address` 且将在 sing-box 1.12.0 中被移除。
|
||||||
|
|
||||||
启用 `auto_route` 时排除自定义路由。
|
启用 `auto_route` 时排除自定义路由。
|
||||||
|
|
||||||
@@ -318,7 +323,7 @@ tun 接口的 IPv6 前缀。
|
|||||||
|
|
||||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||||
|
|
||||||
`inet6_route_exclude_address` 已合并到 `route_exclude_address` 且将在 sing-box 1.11.0 移除。
|
`inet6_route_exclude_address` 已合并到 `route_exclude_address` 且将在 sing-box 1.12.0 中被移除。
|
||||||
|
|
||||||
启用 `auto_route` 时排除自定义路由。
|
启用 `auto_route` 时排除自定义路由。
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ icon: material/alert-decagram
|
|||||||
|
|
||||||
!!! quote "Changes in sing-box 1.11.0"
|
!!! quote "Changes in sing-box 1.11.0"
|
||||||
|
|
||||||
:material-alert-decagram: [override_address](#override_address)
|
:material-delete-clock: [override_address](#override_address)
|
||||||
:material-alert-decagram: [override_port](#override_port)
|
:material-delete-clock: [override_port](#override_port)
|
||||||
|
|
||||||
`direct` outbound send requests directly.
|
`direct` outbound send requests directly.
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ icon: material/delete-clock
|
|||||||
|
|
||||||
WireGuard outbound is deprecated and will be removed in sing-box 1.13.0, check [Migration](/migration/#migrate-wireguard-outbound-to-endpoint).
|
WireGuard outbound is deprecated and will be removed in sing-box 1.13.0, check [Migration](/migration/#migrate-wireguard-outbound-to-endpoint).
|
||||||
|
|
||||||
|
!!! quote "Changes in sing-box 1.11.0"
|
||||||
|
|
||||||
|
:material-delete-alert: [gso](#gso)
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.8.0"
|
!!! quote "Changes in sing-box 1.8.0"
|
||||||
|
|
||||||
:material-plus: [gso](#gso)
|
:material-plus: [gso](#gso)
|
||||||
@@ -20,7 +24,6 @@ icon: material/delete-clock
|
|||||||
"server": "127.0.0.1",
|
"server": "127.0.0.1",
|
||||||
"server_port": 1080,
|
"server_port": 1080,
|
||||||
"system_interface": false,
|
"system_interface": false,
|
||||||
"gso": false,
|
|
||||||
"interface_name": "wg0",
|
"interface_name": "wg0",
|
||||||
"local_address": [
|
"local_address": [
|
||||||
"10.0.0.1/32"
|
"10.0.0.1/32"
|
||||||
@@ -45,6 +48,10 @@ icon: material/delete-clock
|
|||||||
"mtu": 1408,
|
"mtu": 1408,
|
||||||
"network": "tcp",
|
"network": "tcp",
|
||||||
|
|
||||||
|
// Deprecated
|
||||||
|
|
||||||
|
"gso": false,
|
||||||
|
|
||||||
... // Dial Fields
|
... // Dial Fields
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -77,6 +84,10 @@ Custom interface name for system interface.
|
|||||||
|
|
||||||
#### gso
|
#### gso
|
||||||
|
|
||||||
|
!!! failure "Deprecated in sing-box 1.11.0"
|
||||||
|
|
||||||
|
GSO will be automatically enabled when available since sing-box 1.11.0.
|
||||||
|
|
||||||
!!! question "Since sing-box 1.8.0"
|
!!! question "Since sing-box 1.8.0"
|
||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ icon: material/delete-clock
|
|||||||
|
|
||||||
WireGuard 出站已被启用,且将在 sing-box 1.13.0 中被移除,参阅 [迁移指南](/migration/#migrate-wireguard-outbound-to-endpoint)。
|
WireGuard 出站已被启用,且将在 sing-box 1.13.0 中被移除,参阅 [迁移指南](/migration/#migrate-wireguard-outbound-to-endpoint)。
|
||||||
|
|
||||||
|
!!! quote "sing-box 1.11.0 中的更改"
|
||||||
|
|
||||||
|
:material-delete-alert: [gso](#gso)
|
||||||
|
|
||||||
!!! quote "sing-box 1.8.0 中的更改"
|
!!! quote "sing-box 1.8.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [gso](#gso)
|
:material-plus: [gso](#gso)
|
||||||
@@ -20,7 +24,6 @@ icon: material/delete-clock
|
|||||||
"server": "127.0.0.1",
|
"server": "127.0.0.1",
|
||||||
"server_port": 1080,
|
"server_port": 1080,
|
||||||
"system_interface": false,
|
"system_interface": false,
|
||||||
"gso": false,
|
|
||||||
"interface_name": "wg0",
|
"interface_name": "wg0",
|
||||||
"local_address": [
|
"local_address": [
|
||||||
"10.0.0.1/32"
|
"10.0.0.1/32"
|
||||||
@@ -32,6 +35,10 @@ icon: material/delete-clock
|
|||||||
"workers": 4,
|
"workers": 4,
|
||||||
"mtu": 1408,
|
"mtu": 1408,
|
||||||
"network": "tcp",
|
"network": "tcp",
|
||||||
|
|
||||||
|
// 废弃的
|
||||||
|
|
||||||
|
"gso": false,
|
||||||
|
|
||||||
... // 拨号字段
|
... // 拨号字段
|
||||||
}
|
}
|
||||||
@@ -65,6 +72,10 @@ icon: material/delete-clock
|
|||||||
|
|
||||||
#### gso
|
#### gso
|
||||||
|
|
||||||
|
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||||
|
|
||||||
|
自 sing-box 1.11.0 起,GSO 将可用时自动启用。
|
||||||
|
|
||||||
!!! question "自 sing-box 1.8.0 起"
|
!!! question "自 sing-box 1.8.0 起"
|
||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|||||||
@@ -388,7 +388,7 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
|||||||
|
|
||||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||||
|
|
||||||
`rule_set_ipcidr_match_source` 已重命名为 `rule_set_ip_cidr_match_source` 且将在 sing-box 1.11.0 移除。
|
`rule_set_ipcidr_match_source` 已重命名为 `rule_set_ip_cidr_match_source` 且将在 sing-box 1.11.0 中被移除。
|
||||||
|
|
||||||
使规则集中的 `ip_cidr` 规则匹配源 IP。
|
使规则集中的 `ip_cidr` 规则匹配源 IP。
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ See `route-options` fields below.
|
|||||||
"network_strategy": "",
|
"network_strategy": "",
|
||||||
"fallback_delay": "",
|
"fallback_delay": "",
|
||||||
"udp_disable_domain_unmapping": false,
|
"udp_disable_domain_unmapping": false,
|
||||||
"udp_connect": false
|
"udp_connect": false,
|
||||||
|
"udp_timeout": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -86,6 +87,28 @@ do not support receiving UDP packets with domain addresses, such as Surge.
|
|||||||
|
|
||||||
If enabled, attempts to connect UDP connection to the destination instead of listen.
|
If enabled, attempts to connect UDP connection to the destination instead of listen.
|
||||||
|
|
||||||
|
#### udp_timeout
|
||||||
|
|
||||||
|
Timeout for UDP connections.
|
||||||
|
|
||||||
|
Setting a larger value than the UDP timeout in inbounds will have no effect.
|
||||||
|
|
||||||
|
Default value for protocol sniffed connections:
|
||||||
|
|
||||||
|
| Timeout | Protocol |
|
||||||
|
|---------|----------------------|
|
||||||
|
| `10s` | `dns`, `ntp`, `stun` |
|
||||||
|
| `30s` | `quic`, `dtls` |
|
||||||
|
|
||||||
|
If no protocol is sniffed, the following ports will be recognized as protocols by default:
|
||||||
|
|
||||||
|
| Port | Protocol |
|
||||||
|
|------|----------|
|
||||||
|
| 53 | `dns` |
|
||||||
|
| 123 | `ntp` |
|
||||||
|
| 443 | `quic` |
|
||||||
|
| 3478 | `stun` |
|
||||||
|
|
||||||
### reject
|
### reject
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ icon: material/new-box
|
|||||||
"network_strategy": "",
|
"network_strategy": "",
|
||||||
"fallback_delay": "",
|
"fallback_delay": "",
|
||||||
"udp_disable_domain_unmapping": false,
|
"udp_disable_domain_unmapping": false,
|
||||||
"udp_connect": false
|
"udp_connect": false,
|
||||||
|
"udp_timeout": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -84,6 +85,28 @@ icon: material/new-box
|
|||||||
|
|
||||||
如果启用,将尝试将 UDP 连接 connect 到目标而不是 listen。
|
如果启用,将尝试将 UDP 连接 connect 到目标而不是 listen。
|
||||||
|
|
||||||
|
#### udp_timeout
|
||||||
|
|
||||||
|
UDP 连接超时时间。
|
||||||
|
|
||||||
|
设置比入站 UDP 超时更大的值将无效。
|
||||||
|
|
||||||
|
已探测协议连接的默认值:
|
||||||
|
|
||||||
|
| 超时 | 协议 |
|
||||||
|
|-------|----------------------|
|
||||||
|
| `10s` | `dns`, `ntp`, `stun` |
|
||||||
|
| `30s` | `quic`, `dtls` |
|
||||||
|
|
||||||
|
如果没有探测到协议,以下端口将默认识别为协议:
|
||||||
|
|
||||||
|
| 端口 | 协议 |
|
||||||
|
|------|--------|
|
||||||
|
| 53 | `dns` |
|
||||||
|
| 123 | `ntp` |
|
||||||
|
| 443 | `quic` |
|
||||||
|
| 3478 | `stun` |
|
||||||
|
|
||||||
### reject
|
### reject
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ check [Migration](../migration/#migrate-wireguard-outbound-to-endpoint).
|
|||||||
|
|
||||||
Old outbound will be removed in sing-box 1.13.0.
|
Old outbound will be removed in sing-box 1.13.0.
|
||||||
|
|
||||||
|
#### GSO option in TUN
|
||||||
|
|
||||||
|
GSO has no advantages for transparent proxy scenarios, is deprecated and no longer works in TUN.
|
||||||
|
|
||||||
|
Old fields will be removed in sing-box 1.13.0.
|
||||||
|
|
||||||
## 1.10.0
|
## 1.10.0
|
||||||
|
|
||||||
#### TUN address fields are merged
|
#### TUN address fields are merged
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ WireGuard 出站已废弃且可以通过端点替代,
|
|||||||
|
|
||||||
旧出站将在 sing-box 1.13.0 中被移除。
|
旧出站将在 sing-box 1.13.0 中被移除。
|
||||||
|
|
||||||
|
#### TUN 的 GSO 字段
|
||||||
|
|
||||||
|
GSO 对透明代理场景没有优势,已废弃且在 TUN 中不再起作用。
|
||||||
|
|
||||||
|
旧字段将在 sing-box 1.13.0 中被移除。
|
||||||
|
|
||||||
## 1.10.0
|
## 1.10.0
|
||||||
|
|
||||||
#### Match source 规则项已重命名
|
#### Match source 规则项已重命名
|
||||||
|
|||||||
@@ -242,7 +242,6 @@ WireGuard outbound is deprecated and can be replaced by endpoint.
|
|||||||
"system": true,
|
"system": true,
|
||||||
"name": "wg0",
|
"name": "wg0",
|
||||||
"mtu": 1408,
|
"mtu": 1408,
|
||||||
"gso": true,
|
|
||||||
"address": [
|
"address": [
|
||||||
"10.0.0.2/32"
|
"10.0.0.2/32"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -243,7 +243,6 @@ WireGuard 出站已被弃用,且可以被端点替代。
|
|||||||
"system": true,
|
"system": true,
|
||||||
"name": "wg0",
|
"name": "wg0",
|
||||||
"mtu": 1408,
|
"mtu": 1408,
|
||||||
"gso": true,
|
|
||||||
"address": [
|
"address": [
|
||||||
"10.0.0.2/32"
|
"10.0.0.2/32"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func groupRouter(server *Server) http.Handler {
|
|||||||
|
|
||||||
func getGroups(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
func getGroups(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
groups := common.Map(common.Filter(server.outboundManager.Outbounds(), func(it adapter.Outbound) bool {
|
groups := common.Map(common.Filter(server.outbound.Outbounds(), func(it adapter.Outbound) bool {
|
||||||
_, isGroup := it.(adapter.OutboundGroup)
|
_, isGroup := it.(adapter.OutboundGroup)
|
||||||
return isGroup
|
return isGroup
|
||||||
}), func(it adapter.Outbound) *badjson.JSONObject {
|
}), func(it adapter.Outbound) *badjson.JSONObject {
|
||||||
@@ -86,7 +86,7 @@ func getGroupDelay(server *Server) func(w http.ResponseWriter, r *http.Request)
|
|||||||
result, err = urlTestGroup.URLTest(ctx)
|
result, err = urlTestGroup.URLTest(ctx)
|
||||||
} else {
|
} else {
|
||||||
outbounds := common.FilterNotNil(common.Map(outboundGroup.All(), func(it string) adapter.Outbound {
|
outbounds := common.FilterNotNil(common.Map(outboundGroup.All(), func(it string) adapter.Outbound {
|
||||||
itOutbound, _ := server.outboundManager.Outbound(it)
|
itOutbound, _ := server.outbound.Outbound(it)
|
||||||
return itOutbound
|
return itOutbound
|
||||||
}))
|
}))
|
||||||
b, _ := batch.New(ctx, batch.WithConcurrencyNum[any](10))
|
b, _ := batch.New(ctx, batch.WithConcurrencyNum[any](10))
|
||||||
@@ -100,7 +100,7 @@ func getGroupDelay(server *Server) func(w http.ResponseWriter, r *http.Request)
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
checked[realTag] = true
|
checked[realTag] = true
|
||||||
p, loaded := server.outboundManager.Outbound(realTag)
|
p, loaded := server.outbound.Outbound(realTag)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,17 +18,19 @@ func configRouter(server *Server, logFactory log.Factory) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type configSchema struct {
|
type configSchema struct {
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
SocksPort int `json:"socks-port"`
|
SocksPort int `json:"socks-port"`
|
||||||
RedirPort int `json:"redir-port"`
|
RedirPort int `json:"redir-port"`
|
||||||
TProxyPort int `json:"tproxy-port"`
|
TProxyPort int `json:"tproxy-port"`
|
||||||
MixedPort int `json:"mixed-port"`
|
MixedPort int `json:"mixed-port"`
|
||||||
AllowLan bool `json:"allow-lan"`
|
AllowLan bool `json:"allow-lan"`
|
||||||
BindAddress string `json:"bind-address"`
|
BindAddress string `json:"bind-address"`
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
LogLevel string `json:"log-level"`
|
// sing-box added
|
||||||
IPv6 bool `json:"ipv6"`
|
ModeList []string `json:"mode-list"`
|
||||||
Tun map[string]any `json:"tun"`
|
LogLevel string `json:"log-level"`
|
||||||
|
IPv6 bool `json:"ipv6"`
|
||||||
|
Tun map[string]any `json:"tun"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getConfigs(server *Server, logFactory log.Factory) func(w http.ResponseWriter, r *http.Request) {
|
func getConfigs(server *Server, logFactory log.Factory) func(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -41,6 +43,7 @@ func getConfigs(server *Server, logFactory log.Factory) func(w http.ResponseWrit
|
|||||||
}
|
}
|
||||||
render.JSON(w, r, &configSchema{
|
render.JSON(w, r, &configSchema{
|
||||||
Mode: server.mode,
|
Mode: server.mode,
|
||||||
|
ModeList: server.modeList,
|
||||||
BindAddress: "*",
|
BindAddress: "*",
|
||||||
LogLevel: log.FormatLevel(logLevel),
|
LogLevel: log.FormatLevel(logLevel),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func findProxyByName(server *Server) func(next http.Handler) http.Handler {
|
|||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
name := r.Context().Value(CtxKeyProxyName).(string)
|
name := r.Context().Value(CtxKeyProxyName).(string)
|
||||||
proxy, exist := server.outboundManager.Outbound(name)
|
proxy, exist := server.outbound.Outbound(name)
|
||||||
if !exist {
|
if !exist {
|
||||||
render.Status(r, http.StatusNotFound)
|
render.Status(r, http.StatusNotFound)
|
||||||
render.JSON(w, r, ErrNotFound)
|
render.JSON(w, r, ErrNotFound)
|
||||||
@@ -86,9 +86,14 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
|
|||||||
func getProxies(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
func getProxies(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var proxyMap badjson.JSONObject
|
var proxyMap badjson.JSONObject
|
||||||
outbounds := common.Filter(server.outboundManager.Outbounds(), func(detour adapter.Outbound) bool {
|
outbounds := common.Filter(server.outbound.Outbounds(), func(detour adapter.Outbound) bool {
|
||||||
return detour.Tag() != ""
|
return detour.Tag() != ""
|
||||||
})
|
})
|
||||||
|
outbounds = append(outbounds, common.Map(common.Filter(server.endpoint.Endpoints(), func(detour adapter.Endpoint) bool {
|
||||||
|
return detour.Tag() != ""
|
||||||
|
}), func(it adapter.Endpoint) adapter.Outbound {
|
||||||
|
return it
|
||||||
|
})...)
|
||||||
|
|
||||||
allProxies := make([]string, 0, len(outbounds))
|
allProxies := make([]string, 0, len(outbounds))
|
||||||
|
|
||||||
@@ -100,7 +105,7 @@ func getProxies(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
|||||||
allProxies = append(allProxies, detour.Tag())
|
allProxies = append(allProxies, detour.Tag())
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultTag := server.outboundManager.Default().Tag()
|
defaultTag := server.outbound.Default().Tag()
|
||||||
|
|
||||||
sort.SliceStable(allProxies, func(i, j int) bool {
|
sort.SliceStable(allProxies, func(i, j int) bool {
|
||||||
return allProxies[i] == defaultTag
|
return allProxies[i] == defaultTag
|
||||||
|
|||||||
@@ -40,16 +40,17 @@ func init() {
|
|||||||
var _ adapter.ClashServer = (*Server)(nil)
|
var _ adapter.ClashServer = (*Server)(nil)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
router adapter.Router
|
router adapter.Router
|
||||||
outboundManager adapter.OutboundManager
|
outbound adapter.OutboundManager
|
||||||
logger log.Logger
|
endpoint adapter.EndpointManager
|
||||||
httpServer *http.Server
|
logger log.Logger
|
||||||
trafficManager *trafficontrol.Manager
|
httpServer *http.Server
|
||||||
urlTestHistory *urltest.HistoryStorage
|
trafficManager *trafficontrol.Manager
|
||||||
mode string
|
urlTestHistory *urltest.HistoryStorage
|
||||||
modeList []string
|
mode string
|
||||||
modeUpdateHook chan<- struct{}
|
modeList []string
|
||||||
|
modeUpdateHook chan<- struct{}
|
||||||
|
|
||||||
externalController bool
|
externalController bool
|
||||||
externalUI string
|
externalUI string
|
||||||
@@ -61,10 +62,11 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op
|
|||||||
trafficManager := trafficontrol.NewManager()
|
trafficManager := trafficontrol.NewManager()
|
||||||
chiRouter := chi.NewRouter()
|
chiRouter := chi.NewRouter()
|
||||||
s := &Server{
|
s := &Server{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
router: service.FromContext[adapter.Router](ctx),
|
router: service.FromContext[adapter.Router](ctx),
|
||||||
outboundManager: service.FromContext[adapter.OutboundManager](ctx),
|
outbound: service.FromContext[adapter.OutboundManager](ctx),
|
||||||
logger: logFactory.NewLogger("clash-api"),
|
endpoint: service.FromContext[adapter.EndpointManager](ctx),
|
||||||
|
logger: logFactory.NewLogger("clash-api"),
|
||||||
httpServer: &http.Server{
|
httpServer: &http.Server{
|
||||||
Addr: options.ExternalController,
|
Addr: options.ExternalController,
|
||||||
Handler: chiRouter,
|
Handler: chiRouter,
|
||||||
@@ -242,11 +244,11 @@ func (s *Server) TrafficManager() *trafficontrol.Manager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) RoutedConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, matchedRule adapter.Rule, matchOutbound adapter.Outbound) net.Conn {
|
func (s *Server) RoutedConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, matchedRule adapter.Rule, matchOutbound adapter.Outbound) net.Conn {
|
||||||
return trafficontrol.NewTCPTracker(conn, s.trafficManager, metadata, s.outboundManager, matchedRule, matchOutbound)
|
return trafficontrol.NewTCPTracker(conn, s.trafficManager, metadata, s.outbound, matchedRule, matchOutbound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, matchedRule adapter.Rule, matchOutbound adapter.Outbound) N.PacketConn {
|
func (s *Server) RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, matchedRule adapter.Rule, matchOutbound adapter.Outbound) N.PacketConn {
|
||||||
return trafficontrol.NewUDPTracker(conn, s.trafficManager, metadata, s.outboundManager, matchedRule, matchOutbound)
|
return trafficontrol.NewUDPTracker(conn, s.trafficManager, metadata, s.outbound, matchedRule, matchOutbound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func authentication(serverSecret string) func(next http.Handler) http.Handler {
|
func authentication(serverSecret string) func(next http.Handler) http.Handler {
|
||||||
@@ -321,27 +323,29 @@ func traffic(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter,
|
|||||||
tick := time.NewTicker(time.Second)
|
tick := time.NewTicker(time.Second)
|
||||||
defer tick.Stop()
|
defer tick.Stop()
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
var err error
|
uploadTotal, downloadTotal := trafficManager.Total()
|
||||||
for range tick.C {
|
for range tick.C {
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
up, down := trafficManager.Now()
|
uploadTotalNew, downloadTotalNew := trafficManager.Total()
|
||||||
if err := json.NewEncoder(buf).Encode(Traffic{
|
err := json.NewEncoder(buf).Encode(Traffic{
|
||||||
Up: up,
|
Up: uploadTotalNew - uploadTotal,
|
||||||
Down: down,
|
Down: downloadTotalNew - downloadTotal,
|
||||||
}); err != nil {
|
})
|
||||||
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if conn == nil {
|
if conn == nil {
|
||||||
_, err = w.Write(buf.Bytes())
|
_, err = w.Write(buf.Bytes())
|
||||||
w.(http.Flusher).Flush()
|
w.(http.Flusher).Flush()
|
||||||
} else {
|
} else {
|
||||||
err = wsutil.WriteServerText(conn, buf.Bytes())
|
err = wsutil.WriteServerText(conn, buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uploadTotal = uploadTotalNew
|
||||||
|
downloadTotal = downloadTotalNew
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,13 +44,13 @@ func (s *Server) downloadExternalUI() error {
|
|||||||
s.logger.Info("downloading external ui")
|
s.logger.Info("downloading external ui")
|
||||||
var detour adapter.Outbound
|
var detour adapter.Outbound
|
||||||
if s.externalUIDownloadDetour != "" {
|
if s.externalUIDownloadDetour != "" {
|
||||||
outbound, loaded := s.outboundManager.Outbound(s.externalUIDownloadDetour)
|
outbound, loaded := s.outbound.Outbound(s.externalUIDownloadDetour)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return E.New("detour outbound not found: ", s.externalUIDownloadDetour)
|
return E.New("detour outbound not found: ", s.externalUIDownloadDetour)
|
||||||
}
|
}
|
||||||
detour = outbound
|
detour = outbound
|
||||||
} else {
|
} else {
|
||||||
outbound := s.outboundManager.Default()
|
outbound := s.outbound.Default()
|
||||||
detour = outbound
|
detour = outbound
|
||||||
}
|
}
|
||||||
httpClient := &http.Client{
|
httpClient := &http.Client{
|
||||||
|
|||||||
@@ -16,30 +16,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
uploadTemp atomic.Int64
|
|
||||||
downloadTemp atomic.Int64
|
|
||||||
uploadBlip atomic.Int64
|
|
||||||
downloadBlip atomic.Int64
|
|
||||||
uploadTotal atomic.Int64
|
uploadTotal atomic.Int64
|
||||||
downloadTotal atomic.Int64
|
downloadTotal atomic.Int64
|
||||||
|
|
||||||
connections compatible.Map[uuid.UUID, Tracker]
|
connections compatible.Map[uuid.UUID, Tracker]
|
||||||
closedConnectionsAccess sync.Mutex
|
closedConnectionsAccess sync.Mutex
|
||||||
closedConnections list.List[TrackerMetadata]
|
closedConnections list.List[TrackerMetadata]
|
||||||
ticker *time.Ticker
|
|
||||||
done chan struct{}
|
|
||||||
// process *process.Process
|
// process *process.Process
|
||||||
memory uint64
|
memory uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewManager() *Manager {
|
func NewManager() *Manager {
|
||||||
manager := &Manager{
|
return &Manager{}
|
||||||
ticker: time.NewTicker(time.Second),
|
|
||||||
done: make(chan struct{}),
|
|
||||||
// process: &process.Process{Pid: int32(os.Getpid())},
|
|
||||||
}
|
|
||||||
go manager.handle()
|
|
||||||
return manager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) Join(c Tracker) {
|
func (m *Manager) Join(c Tracker) {
|
||||||
@@ -61,19 +49,13 @@ func (m *Manager) Leave(c Tracker) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) PushUploaded(size int64) {
|
func (m *Manager) PushUploaded(size int64) {
|
||||||
m.uploadTemp.Add(size)
|
|
||||||
m.uploadTotal.Add(size)
|
m.uploadTotal.Add(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) PushDownloaded(size int64) {
|
func (m *Manager) PushDownloaded(size int64) {
|
||||||
m.downloadTemp.Add(size)
|
|
||||||
m.downloadTotal.Add(size)
|
m.downloadTotal.Add(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) Now() (up int64, down int64) {
|
|
||||||
return m.uploadBlip.Load(), m.downloadBlip.Load()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) Total() (up int64, down int64) {
|
func (m *Manager) Total() (up int64, down int64) {
|
||||||
return m.uploadTotal.Load(), m.downloadTotal.Load()
|
return m.uploadTotal.Load(), m.downloadTotal.Load()
|
||||||
}
|
}
|
||||||
@@ -127,36 +109,10 @@ func (m *Manager) Snapshot() *Snapshot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) ResetStatistic() {
|
func (m *Manager) ResetStatistic() {
|
||||||
m.uploadTemp.Store(0)
|
|
||||||
m.uploadBlip.Store(0)
|
|
||||||
m.uploadTotal.Store(0)
|
m.uploadTotal.Store(0)
|
||||||
m.downloadTemp.Store(0)
|
|
||||||
m.downloadBlip.Store(0)
|
|
||||||
m.downloadTotal.Store(0)
|
m.downloadTotal.Store(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) handle() {
|
|
||||||
var uploadTemp int64
|
|
||||||
var downloadTemp int64
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-m.done:
|
|
||||||
return
|
|
||||||
case <-m.ticker.C:
|
|
||||||
}
|
|
||||||
uploadTemp = m.uploadTemp.Swap(0)
|
|
||||||
downloadTemp = m.downloadTemp.Swap(0)
|
|
||||||
m.uploadBlip.Store(uploadTemp)
|
|
||||||
m.downloadBlip.Store(downloadTemp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) Close() error {
|
|
||||||
m.ticker.Stop()
|
|
||||||
close(m.done)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Snapshot struct {
|
type Snapshot struct {
|
||||||
Download int64
|
Download int64
|
||||||
Upload int64
|
Upload int64
|
||||||
|
|||||||
@@ -33,17 +33,31 @@ func (n Note) Impending() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n Note) Message() string {
|
func (n Note) Message() string {
|
||||||
return F.ToString(
|
if n.MigrationLink != "" {
|
||||||
n.Description, " is deprecated in sing-box ", n.DeprecatedVersion,
|
return F.ToString(
|
||||||
" and will be removed in sing-box ", n.ScheduledVersion, ", please checkout documentation for migration.",
|
n.Description, " is deprecated in sing-box ", n.DeprecatedVersion,
|
||||||
)
|
" and will be removed in sing-box ", n.ScheduledVersion, ", please checkout documentation for migration.",
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return F.ToString(
|
||||||
|
n.Description, " is deprecated in sing-box ", n.DeprecatedVersion,
|
||||||
|
" and will be removed in sing-box ", n.ScheduledVersion, ".",
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n Note) MessageWithLink() string {
|
func (n Note) MessageWithLink() string {
|
||||||
return F.ToString(
|
if n.MigrationLink != "" {
|
||||||
n.Description, " is deprecated in sing-box ", n.DeprecatedVersion,
|
return F.ToString(
|
||||||
" and will be removed in sing-box ", n.ScheduledVersion, ", checkout documentation for migration: ", n.MigrationLink,
|
n.Description, " is deprecated in sing-box ", n.DeprecatedVersion,
|
||||||
)
|
" and will be removed in sing-box ", n.ScheduledVersion, ", checkout documentation for migration: ", n.MigrationLink,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return F.ToString(
|
||||||
|
n.Description, " is deprecated in sing-box ", n.DeprecatedVersion,
|
||||||
|
" and will be removed in sing-box ", n.ScheduledVersion, ".",
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var OptionBadMatchSource = Note{
|
var OptionBadMatchSource = Note{
|
||||||
@@ -118,6 +132,23 @@ var OptionWireGuardOutbound = Note{
|
|||||||
MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-wireguard-outbound-to-endpoint",
|
MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-wireguard-outbound-to-endpoint",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var OptionWireGuardGSO = Note{
|
||||||
|
Name: "wireguard-gso",
|
||||||
|
Description: "GSO option in wireguard outbound",
|
||||||
|
DeprecatedVersion: "1.11.0",
|
||||||
|
ScheduledVersion: "1.13.0",
|
||||||
|
EnvName: "WIREGUARD_GSO",
|
||||||
|
MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-wireguard-outbound-to-endpoint",
|
||||||
|
}
|
||||||
|
|
||||||
|
var OptionTUNGSO = Note{
|
||||||
|
Name: "tun-gso",
|
||||||
|
Description: "GSO option in tun",
|
||||||
|
DeprecatedVersion: "1.11.0",
|
||||||
|
ScheduledVersion: "1.12.0",
|
||||||
|
EnvName: "TUN_GSO",
|
||||||
|
}
|
||||||
|
|
||||||
var Options = []Note{
|
var Options = []Note{
|
||||||
OptionBadMatchSource,
|
OptionBadMatchSource,
|
||||||
OptionGEOIP,
|
OptionGEOIP,
|
||||||
@@ -127,4 +158,6 @@ var Options = []Note{
|
|||||||
OptionInboundOptions,
|
OptionInboundOptions,
|
||||||
OptionDestinationOverrideFields,
|
OptionDestinationOverrideFields,
|
||||||
OptionWireGuardOutbound,
|
OptionWireGuardOutbound,
|
||||||
|
OptionWireGuardGSO,
|
||||||
|
OptionTUNGSO,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,5 +34,5 @@ func (f *stderrManager) ReportDeprecated(feature Note) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
f.logger.Error(feature.MessageWithLink())
|
f.logger.Error(feature.MessageWithLink())
|
||||||
f.logger.Fatal("to continuing using this feature, set ENABLE_DEPRECATED_" + feature.EnvName + "=true")
|
f.logger.Fatal("to continuing using this feature, set environment variable ENABLE_DEPRECATED_" + feature.EnvName + "=true")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
@@ -113,11 +114,24 @@ func (c *CommandClient) Connect() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.handler.Connected()
|
if C.FixAndroidStack {
|
||||||
c.handler.InitializeClashMode(newIterator(modeList), currentMode)
|
go func() {
|
||||||
|
c.handler.Connected()
|
||||||
|
c.handler.InitializeClashMode(newIterator(modeList), currentMode)
|
||||||
|
if len(modeList) == 0 {
|
||||||
|
conn.Close()
|
||||||
|
c.handler.Disconnected(os.ErrInvalid.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
} else {
|
||||||
|
c.handler.Connected()
|
||||||
|
c.handler.InitializeClashMode(newIterator(modeList), currentMode)
|
||||||
|
if len(modeList) == 0 {
|
||||||
|
conn.Close()
|
||||||
|
c.handler.Disconnected(os.ErrInvalid.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(modeList) == 0 {
|
if len(modeList) == 0 {
|
||||||
conn.Close()
|
|
||||||
c.handler.Disconnected(os.ErrInvalid.Error())
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
go c.handleModeConn(conn)
|
go c.handleModeConn(conn)
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ func (s *CommandServer) readStatus() StatusMessage {
|
|||||||
if s.service != nil {
|
if s.service != nil {
|
||||||
message.TrafficAvailable = true
|
message.TrafficAvailable = true
|
||||||
trafficManager := s.service.clashServer.(*clashapi.Server).TrafficManager()
|
trafficManager := s.service.clashServer.(*clashapi.Server).TrafficManager()
|
||||||
message.Uplink, message.Downlink = trafficManager.Now()
|
|
||||||
message.UplinkTotal, message.DownlinkTotal = trafficManager.Total()
|
message.UplinkTotal, message.DownlinkTotal = trafficManager.Total()
|
||||||
message.ConnectionsIn = int32(trafficManager.ConnectionsLen())
|
message.ConnectionsIn = int32(trafficManager.ConnectionsLen())
|
||||||
}
|
}
|
||||||
@@ -50,8 +49,11 @@ func (s *CommandServer) handleStatusConn(conn net.Conn) error {
|
|||||||
ticker := time.NewTicker(time.Duration(interval))
|
ticker := time.NewTicker(time.Duration(interval))
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
ctx := connKeepAlive(conn)
|
ctx := connKeepAlive(conn)
|
||||||
|
status := s.readStatus()
|
||||||
|
uploadTotal := status.UplinkTotal
|
||||||
|
downloadTotal := status.DownlinkTotal
|
||||||
for {
|
for {
|
||||||
err = binary.Write(conn, binary.BigEndian, s.readStatus())
|
err = binary.Write(conn, binary.BigEndian, status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -60,6 +62,13 @@ func (s *CommandServer) handleStatusConn(conn net.Conn) error {
|
|||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
}
|
}
|
||||||
|
status = s.readStatus()
|
||||||
|
upload := status.UplinkTotal - uploadTotal
|
||||||
|
download := status.DownlinkTotal - downloadTotal
|
||||||
|
uploadTotal = status.UplinkTotal
|
||||||
|
downloadTotal = status.DownlinkTotal
|
||||||
|
status.Uplink = upload
|
||||||
|
status.Downlink = download
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,17 +130,17 @@ func (s *platformInterfaceStub) SendNotification(notification *platform.Notifica
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatConfig(configContent string) (string, error) {
|
func FormatConfig(configContent string) (*StringBox, error) {
|
||||||
options, err := parseConfig(box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry()), configContent)
|
options, err := parseConfig(box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry()), configContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
encoder := json.NewEncoder(&buffer)
|
encoder := json.NewEncoder(&buffer)
|
||||||
encoder.SetIndent("", " ")
|
encoder.SetIndent("", " ")
|
||||||
err = encoder.Encode(options)
|
err = encoder.Encode(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
return buffer.String(), nil
|
return wrapString(buffer.String()), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,8 +50,7 @@ type HTTPRequest interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type HTTPResponse interface {
|
type HTTPResponse interface {
|
||||||
GetContent() ([]byte, error)
|
GetContent() (*StringBox, error)
|
||||||
GetContentString() (string, error)
|
|
||||||
WriteTo(path string) error
|
WriteTo(path string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,27 +209,22 @@ type httpResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpResponse) errorString() string {
|
func (h *httpResponse) errorString() string {
|
||||||
content, err := h.GetContentString()
|
content, err := h.GetContent()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Sprint("HTTP ", h.Status)
|
return fmt.Sprint("HTTP ", h.Status)
|
||||||
}
|
}
|
||||||
return fmt.Sprint("HTTP ", h.Status, ": ", content)
|
return fmt.Sprint("HTTP ", h.Status, ": ", content)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpResponse) GetContent() ([]byte, error) {
|
func (h *httpResponse) GetContent() (*StringBox, error) {
|
||||||
h.getContentOnce.Do(func() {
|
h.getContentOnce.Do(func() {
|
||||||
defer h.Body.Close()
|
defer h.Body.Close()
|
||||||
h.content, h.contentError = io.ReadAll(h.Body)
|
h.content, h.contentError = io.ReadAll(h.Body)
|
||||||
})
|
})
|
||||||
return h.content, h.contentError
|
if h.contentError != nil {
|
||||||
}
|
return nil, h.contentError
|
||||||
|
|
||||||
func (h *httpResponse) GetContentString() (string, error) {
|
|
||||||
content, err := h.GetContent()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
return string(content), nil
|
return wrapString(string(h.content)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpResponse) WriteTo(path string) error {
|
func (h *httpResponse) WriteTo(path string) error {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package libbox
|
package libbox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
@@ -55,6 +56,14 @@ func (m *platformDefaultInterfaceMonitor) UnregisterCallback(element *list.Eleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32, isExpensive bool, isConstrained bool) {
|
func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32, isExpensive bool, isConstrained bool) {
|
||||||
|
if C.FixAndroidStack {
|
||||||
|
go m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)
|
||||||
|
} else {
|
||||||
|
m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *platformDefaultInterfaceMonitor) updateDefaultInterface(interfaceName string, interfaceIndex32 int32, isExpensive bool, isConstrained bool) {
|
||||||
m.isExpensive = isExpensive
|
m.isExpensive = isExpensive
|
||||||
m.isConstrained = isConstrained
|
m.isConstrained = isConstrained
|
||||||
err := m.networkManager.UpdateInterfaces()
|
err := m.networkManager.UpdateInterfaces()
|
||||||
|
|||||||
12
experimental/libbox/panic.go
Normal file
12
experimental/libbox/panic.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package libbox
|
||||||
|
|
||||||
|
// https://github.com/golang/go/issues/46893
|
||||||
|
// TODO: remove after `bulkBarrierPreWrite: unaligned arguments` fixed
|
||||||
|
|
||||||
|
type StringBox struct {
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapString(value string) *StringBox {
|
||||||
|
return &StringBox{Value: value}
|
||||||
|
}
|
||||||
@@ -81,23 +81,36 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *BoxService) Start() error {
|
func (s *BoxService) Start() error {
|
||||||
return s.instance.Start()
|
if C.FixAndroidStack {
|
||||||
|
var err error
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
err = s.instance.Start()
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
<-done
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
return s.instance.Start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BoxService) Close() error {
|
func (s *BoxService) Close() error {
|
||||||
done := make(chan struct{})
|
|
||||||
defer close(done)
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case <-done:
|
|
||||||
return
|
|
||||||
case <-time.After(C.FatalStopTimeout):
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
s.cancel()
|
s.cancel()
|
||||||
s.urlTestHistoryStorage.Close()
|
s.urlTestHistoryStorage.Close()
|
||||||
return s.instance.Close()
|
var err error
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
err = s.instance.Close()
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
return err
|
||||||
|
case <-time.After(C.FatalStopTimeout):
|
||||||
|
os.Exit(1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BoxService) NeedWIFIState() bool {
|
func (s *BoxService) NeedWIFIState() bool {
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ func ClearServiceError() {
|
|||||||
os.Remove(serviceErrorPath())
|
os.Remove(serviceErrorPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadServiceError() (string, error) {
|
func ReadServiceError() (*StringBox, error) {
|
||||||
data, err := os.ReadFile(serviceErrorPath())
|
data, err := os.ReadFile(serviceErrorPath())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
os.Remove(serviceErrorPath())
|
os.Remove(serviceErrorPath())
|
||||||
}
|
}
|
||||||
return string(data), err
|
return wrapString(string(data)), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func WriteServiceError(message string) error {
|
func WriteServiceError(message string) error {
|
||||||
|
|||||||
15
go.mod
15
go.mod
@@ -22,21 +22,21 @@ require (
|
|||||||
github.com/sagernet/cors v1.2.1
|
github.com/sagernet/cors v1.2.1
|
||||||
github.com/sagernet/fswatch v0.1.1
|
github.com/sagernet/fswatch v0.1.1
|
||||||
github.com/sagernet/gomobile v0.1.4
|
github.com/sagernet/gomobile v0.1.4
|
||||||
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3
|
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
|
||||||
github.com/sagernet/quic-go v0.48.1-beta.1
|
github.com/sagernet/quic-go v0.48.2-beta.1
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||||
github.com/sagernet/sing v0.6.0-alpha.18
|
github.com/sagernet/sing v0.6.0-beta.6
|
||||||
github.com/sagernet/sing-dns v0.4.0-alpha.3
|
github.com/sagernet/sing-dns v0.4.0-beta.1
|
||||||
github.com/sagernet/sing-mux v0.3.0-alpha.1
|
github.com/sagernet/sing-mux v0.3.0-alpha.1
|
||||||
github.com/sagernet/sing-quic v0.4.0-alpha.4
|
github.com/sagernet/sing-quic v0.4.0-alpha.4
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||||
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2
|
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2
|
||||||
github.com/sagernet/sing-tun v0.6.0-alpha.10
|
github.com/sagernet/sing-tun v0.6.0-beta.2
|
||||||
github.com/sagernet/sing-vmess v0.1.12
|
github.com/sagernet/sing-vmess v0.2.0-beta.1
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||||
github.com/sagernet/utls v1.6.7
|
github.com/sagernet/utls v1.6.7
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.2
|
github.com/sagernet/wireguard-go v0.0.1-beta.5
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
@@ -92,6 +92,7 @@ require (
|
|||||||
golang.org/x/text v0.20.0 // indirect
|
golang.org/x/text v0.20.0 // indirect
|
||||||
golang.org/x/time v0.7.0 // indirect
|
golang.org/x/time v0.7.0 // indirect
|
||||||
golang.org/x/tools v0.24.0 // indirect
|
golang.org/x/tools v0.24.0 // indirect
|
||||||
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
|||||||
30
go.sum
30
go.sum
@@ -99,21 +99,21 @@ github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQ
|
|||||||
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
||||||
github.com/sagernet/gomobile v0.1.4 h1:WzX9ka+iHdupMgy2Vdich+OAt7TM8C2cZbIbzNjBrJY=
|
github.com/sagernet/gomobile v0.1.4 h1:WzX9ka+iHdupMgy2Vdich+OAt7TM8C2cZbIbzNjBrJY=
|
||||||
github.com/sagernet/gomobile v0.1.4/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
github.com/sagernet/gomobile v0.1.4/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
||||||
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3 h1:RxEz7LhPNiF/gX/Hg+OXr5lqsM9iVAgmaK1L1vzlDRM=
|
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff h1:mlohw3360Wg1BNGook/UHnISXhUx4Gd/3tVLs5T0nSs=
|
||||||
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw=
|
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw=
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
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 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
||||||
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
||||||
github.com/sagernet/quic-go v0.48.1-beta.1 h1:ElPaV5yzlXIKZpqFMAcUGax6vddi3zt4AEpT94Z0vwo=
|
github.com/sagernet/quic-go v0.48.2-beta.1 h1:W0plrLWa1XtOWDTdX3CJwxmQuxkya12nN5BRGZ87kEg=
|
||||||
github.com/sagernet/quic-go v0.48.1-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k=
|
github.com/sagernet/quic-go v0.48.2-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||||
github.com/sagernet/sing v0.6.0-alpha.18 h1:ih4CurU8KvbhfagYjSqVrE2LR0oBSXSZTNH2sAGPGiM=
|
github.com/sagernet/sing v0.6.0-beta.6 h1:IFnTCG06Z5rLMZJqw1ZmDncDl2N9gsVw0MGvgakrpg8=
|
||||||
github.com/sagernet/sing v0.6.0-alpha.18/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.6.0-beta.6/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-dns v0.4.0-alpha.3 h1:TcAQdz68Gs28VD9o9zDIW7IS8A9LZDruTPI9g9JbGHA=
|
github.com/sagernet/sing-dns v0.4.0-beta.1 h1:W1XkdhigwxDOMgMDVB+9kdomCpb7ExsZfB4acPcTZFY=
|
||||||
github.com/sagernet/sing-dns v0.4.0-alpha.3/go.mod h1:9LHcYKg2bGQpbtXrfNbopz8ok/zBK9ljiI2kmFG9JKg=
|
github.com/sagernet/sing-dns v0.4.0-beta.1/go.mod h1:8wuFcoFkWM4vJuQyg8e97LyvDwe0/Vl7G839WLcKDs8=
|
||||||
github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=
|
github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=
|
||||||
github.com/sagernet/sing-mux v0.3.0-alpha.1/go.mod h1:FTcImmdfW38Lz7b+HQ+mxxOth1lz4ao8uEnz+MwIJQE=
|
github.com/sagernet/sing-mux v0.3.0-alpha.1/go.mod h1:FTcImmdfW38Lz7b+HQ+mxxOth1lz4ao8uEnz+MwIJQE=
|
||||||
github.com/sagernet/sing-quic v0.4.0-alpha.4 h1:P9xAx3nIfcqb9M8jfgs0uLm+VxCcaY++FCqaBfHY3dQ=
|
github.com/sagernet/sing-quic v0.4.0-alpha.4 h1:P9xAx3nIfcqb9M8jfgs0uLm+VxCcaY++FCqaBfHY3dQ=
|
||||||
@@ -124,16 +124,16 @@ github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wK
|
|||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 h1:RPrpgAdkP5td0vLfS5ldvYosFjSsZtRPxiyLV6jyKg0=
|
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 h1:RPrpgAdkP5td0vLfS5ldvYosFjSsZtRPxiyLV6jyKg0=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2/go.mod h1:0j5XlzKxaWRIEjc1uiSKmVoWb0k+L9QgZVb876+thZA=
|
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2/go.mod h1:0j5XlzKxaWRIEjc1uiSKmVoWb0k+L9QgZVb876+thZA=
|
||||||
github.com/sagernet/sing-tun v0.6.0-alpha.10 h1:kJOMUR6VKHkTrtJ+kPJVsCqrJYmW0nTRJLYv+Or7lNA=
|
github.com/sagernet/sing-tun v0.6.0-beta.2 h1:GK7r2jWKm7RhlJGTq4QadgFcebQia1c3BO3OlYMcQJ0=
|
||||||
github.com/sagernet/sing-tun v0.6.0-alpha.10/go.mod h1:UmZpZ06gItrbOFLhyeZsilHKQDa5h4NSQy8LalkTkXQ=
|
github.com/sagernet/sing-tun v0.6.0-beta.2/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
||||||
github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg=
|
github.com/sagernet/sing-vmess v0.2.0-beta.1 h1:5sXQ23uwNlZuDvygzi0dFtnG0Csm/SNqTjAHXJkpuj4=
|
||||||
github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I=
|
github.com/sagernet/sing-vmess v0.2.0-beta.1/go.mod h1:fLyE1emIcvQ5DV8reFWnufquZ7MkCSYM5ThodsR9NrQ=
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
|
||||||
github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=
|
github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=
|
||||||
github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM=
|
github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM=
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.2 h1:afmDgfCL2Esc+2EYtdcJFepTWHX9+kZnosC0A84VJ9s=
|
github.com/sagernet/wireguard-go v0.0.1-beta.5 h1:aBEsxJUMEONwOZqKPIkuAcv4zJV5p6XlzEN04CF0FXc=
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.2/go.mod h1:8xfewtQJZ1g3HeMQbLpJxTjyTiE3FL+Joq5LQoKLFEw=
|
github.com/sagernet/wireguard-go v0.0.1-beta.5/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo=
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
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/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
|
||||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||||
@@ -195,6 +195,8 @@ golang.org/x/time v0.7.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.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||||
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||||
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
package option
|
package option
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/json"
|
||||||
|
"github.com/sagernet/sing/common/json/badjson"
|
||||||
|
"github.com/sagernet/sing/common/json/badoption"
|
||||||
|
)
|
||||||
|
|
||||||
type Hysteria2InboundOptions struct {
|
type Hysteria2InboundOptions struct {
|
||||||
ListenOptions
|
ListenOptions
|
||||||
UpMbps int `json:"up_mbps,omitempty"`
|
UpMbps int `json:"up_mbps,omitempty"`
|
||||||
@@ -8,8 +18,8 @@ type Hysteria2InboundOptions struct {
|
|||||||
Users []Hysteria2User `json:"users,omitempty"`
|
Users []Hysteria2User `json:"users,omitempty"`
|
||||||
IgnoreClientBandwidth bool `json:"ignore_client_bandwidth,omitempty"`
|
IgnoreClientBandwidth bool `json:"ignore_client_bandwidth,omitempty"`
|
||||||
InboundTLSOptionsContainer
|
InboundTLSOptionsContainer
|
||||||
Masquerade string `json:"masquerade,omitempty"`
|
Masquerade *Hysteria2Masquerade `json:"masquerade,omitempty"`
|
||||||
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Hysteria2Obfs struct {
|
type Hysteria2Obfs struct {
|
||||||
@@ -22,6 +32,82 @@ type Hysteria2User struct {
|
|||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type _Hysteria2Masquerade struct {
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
FileOptions Hysteria2MasqueradeFile `json:"-"`
|
||||||
|
ProxyOptions Hysteria2MasqueradeProxy `json:"-"`
|
||||||
|
StringOptions Hysteria2MasqueradeString `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Hysteria2Masquerade _Hysteria2Masquerade
|
||||||
|
|
||||||
|
func (m Hysteria2Masquerade) MarshalJSON() ([]byte, error) {
|
||||||
|
var v any
|
||||||
|
switch m.Type {
|
||||||
|
case C.Hysterai2MasqueradeTypeFile:
|
||||||
|
v = m.FileOptions
|
||||||
|
case C.Hysterai2MasqueradeTypeProxy:
|
||||||
|
v = m.ProxyOptions
|
||||||
|
case C.Hysterai2MasqueradeTypeString:
|
||||||
|
v = m.StringOptions
|
||||||
|
default:
|
||||||
|
return nil, E.New("unknown masquerade type: ", m.Type)
|
||||||
|
}
|
||||||
|
return badjson.MarshallObjects((_Hysteria2Masquerade)(m), v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Hysteria2Masquerade) UnmarshalJSON(bytes []byte) error {
|
||||||
|
var urlString string
|
||||||
|
err := json.Unmarshal(bytes, &urlString)
|
||||||
|
if err == nil {
|
||||||
|
masqueradeURL, err := url.Parse(urlString)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "invalid masquerade URL")
|
||||||
|
}
|
||||||
|
switch masqueradeURL.Scheme {
|
||||||
|
case "file":
|
||||||
|
m.Type = C.Hysterai2MasqueradeTypeFile
|
||||||
|
m.FileOptions.Directory = masqueradeURL.Path
|
||||||
|
case "http", "https":
|
||||||
|
m.Type = C.Hysterai2MasqueradeTypeProxy
|
||||||
|
m.ProxyOptions.URL = urlString
|
||||||
|
default:
|
||||||
|
return E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(bytes, (*_Hysteria2Masquerade)(m))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var v any
|
||||||
|
switch m.Type {
|
||||||
|
case C.Hysterai2MasqueradeTypeFile:
|
||||||
|
v = &m.FileOptions
|
||||||
|
case C.Hysterai2MasqueradeTypeProxy:
|
||||||
|
v = &m.ProxyOptions
|
||||||
|
case C.Hysterai2MasqueradeTypeString:
|
||||||
|
v = &m.StringOptions
|
||||||
|
default:
|
||||||
|
return E.New("unknown masquerade type: ", m.Type)
|
||||||
|
}
|
||||||
|
return badjson.UnmarshallExcluded(bytes, (*_Hysteria2Masquerade)(m), v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Hysteria2MasqueradeFile struct {
|
||||||
|
Directory string `json:"directory"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Hysteria2MasqueradeProxy struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
RewriteHost bool `json:"rewrite_host,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Hysteria2MasqueradeString struct {
|
||||||
|
StatusCode int `json:"status_code,omitempty"`
|
||||||
|
Headers badoption.HTTPHeader `json:"headers,omitempty"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
type Hysteria2OutboundOptions struct {
|
type Hysteria2OutboundOptions struct {
|
||||||
DialerOptions
|
DialerOptions
|
||||||
ServerOptions
|
ServerOptions
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ type RouteOptions struct {
|
|||||||
AutoDetectInterface bool `json:"auto_detect_interface,omitempty"`
|
AutoDetectInterface bool `json:"auto_detect_interface,omitempty"`
|
||||||
OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"`
|
OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"`
|
||||||
DefaultInterface string `json:"default_interface,omitempty"`
|
DefaultInterface string `json:"default_interface,omitempty"`
|
||||||
DefaultMark uint32 `json:"default_mark,omitempty"`
|
DefaultMark FwMark `json:"default_mark,omitempty"`
|
||||||
DefaultNetworkStrategy NetworkStrategy `json:"default_network_strategy,omitempty"`
|
DefaultNetworkStrategy NetworkStrategy `json:"default_network_strategy,omitempty"`
|
||||||
DefaultNetworkType badoption.Listable[InterfaceType] `json:"default_network_type,omitempty"`
|
DefaultNetworkType badoption.Listable[InterfaceType] `json:"default_network_type,omitempty"`
|
||||||
DefaultFallbackNetworkType badoption.Listable[InterfaceType] `json:"default_fallback_network_type,omitempty"`
|
DefaultFallbackNetworkType badoption.Listable[InterfaceType] `json:"default_fallback_network_type,omitempty"`
|
||||||
|
|||||||
@@ -148,8 +148,9 @@ type RawRouteOptionsActionOptions struct {
|
|||||||
NetworkStrategy NetworkStrategy `json:"network_strategy,omitempty"`
|
NetworkStrategy NetworkStrategy `json:"network_strategy,omitempty"`
|
||||||
FallbackDelay uint32 `json:"fallback_delay,omitempty"`
|
FallbackDelay uint32 `json:"fallback_delay,omitempty"`
|
||||||
|
|
||||||
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
||||||
UDPConnect bool `json:"udp_connect,omitempty"`
|
UDPConnect bool `json:"udp_connect,omitempty"`
|
||||||
|
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RouteOptionsActionOptions RawRouteOptionsActionOptions
|
type RouteOptionsActionOptions RawRouteOptionsActionOptions
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
type TunInboundOptions struct {
|
type TunInboundOptions struct {
|
||||||
InterfaceName string `json:"interface_name,omitempty"`
|
InterfaceName string `json:"interface_name,omitempty"`
|
||||||
MTU uint32 `json:"mtu,omitempty"`
|
MTU uint32 `json:"mtu,omitempty"`
|
||||||
GSO bool `json:"gso,omitempty"`
|
|
||||||
Address badoption.Listable[netip.Prefix] `json:"address,omitempty"`
|
Address badoption.Listable[netip.Prefix] `json:"address,omitempty"`
|
||||||
AutoRoute bool `json:"auto_route,omitempty"`
|
AutoRoute bool `json:"auto_route,omitempty"`
|
||||||
IPRoute2TableIndex int `json:"iproute2_table_index,omitempty"`
|
IPRoute2TableIndex int `json:"iproute2_table_index,omitempty"`
|
||||||
@@ -35,12 +34,13 @@ type TunInboundOptions struct {
|
|||||||
IncludeAndroidUser badoption.Listable[int] `json:"include_android_user,omitempty"`
|
IncludeAndroidUser badoption.Listable[int] `json:"include_android_user,omitempty"`
|
||||||
IncludePackage badoption.Listable[string] `json:"include_package,omitempty"`
|
IncludePackage badoption.Listable[string] `json:"include_package,omitempty"`
|
||||||
ExcludePackage badoption.Listable[string] `json:"exclude_package,omitempty"`
|
ExcludePackage badoption.Listable[string] `json:"exclude_package,omitempty"`
|
||||||
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
|
|
||||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
||||||
Stack string `json:"stack,omitempty"`
|
Stack string `json:"stack,omitempty"`
|
||||||
Platform *TunPlatformOptions `json:"platform,omitempty"`
|
Platform *TunPlatformOptions `json:"platform,omitempty"`
|
||||||
InboundOptions
|
InboundOptions
|
||||||
|
|
||||||
|
// Deprecated: removed
|
||||||
|
GSO bool `json:"gso,omitempty"`
|
||||||
// Deprecated: merged to Address
|
// Deprecated: merged to Address
|
||||||
Inet4Address badoption.Listable[netip.Prefix] `json:"inet4_address,omitempty"`
|
Inet4Address badoption.Listable[netip.Prefix] `json:"inet4_address,omitempty"`
|
||||||
// Deprecated: merged to Address
|
// Deprecated: merged to Address
|
||||||
@@ -53,6 +53,8 @@ type TunInboundOptions struct {
|
|||||||
Inet4RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"inet4_route_exclude_address,omitempty"`
|
Inet4RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"inet4_route_exclude_address,omitempty"`
|
||||||
// Deprecated: merged to RouteExcludeAddress
|
// Deprecated: merged to RouteExcludeAddress
|
||||||
Inet6RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"inet6_route_exclude_address,omitempty"`
|
Inet6RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"inet6_route_exclude_address,omitempty"`
|
||||||
|
// Deprecated: removed
|
||||||
|
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FwMark uint32
|
type FwMark uint32
|
||||||
|
|||||||
@@ -10,12 +10,11 @@ type WireGuardEndpointOptions struct {
|
|||||||
System bool `json:"system,omitempty"`
|
System bool `json:"system,omitempty"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
MTU uint32 `json:"mtu,omitempty"`
|
MTU uint32 `json:"mtu,omitempty"`
|
||||||
GSO bool `json:"gso,omitempty"`
|
|
||||||
Address badoption.Listable[netip.Prefix] `json:"address"`
|
Address badoption.Listable[netip.Prefix] `json:"address"`
|
||||||
PrivateKey string `json:"private_key"`
|
PrivateKey string `json:"private_key"`
|
||||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||||
Peers []WireGuardPeer `json:"peers,omitempty"`
|
Peers []WireGuardPeer `json:"peers,omitempty"`
|
||||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
|
||||||
Workers int `json:"workers,omitempty"`
|
Workers int `json:"workers,omitempty"`
|
||||||
DialerOptions
|
DialerOptions
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,9 @@ func (i *Inbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (i *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
|
metadata.Inbound = i.Tag()
|
||||||
|
metadata.InboundType = i.Type()
|
||||||
|
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr())
|
||||||
switch i.overrideOption {
|
switch i.overrideOption {
|
||||||
case 1:
|
case 1:
|
||||||
metadata.Destination = i.overrideDestination
|
metadata.Destination = i.overrideDestination
|
||||||
@@ -105,11 +108,9 @@ func (i *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata a
|
|||||||
case 3:
|
case 3:
|
||||||
metadata.Destination.Port = i.overrideDestination.Port
|
metadata.Destination.Port = i.overrideDestination.Port
|
||||||
}
|
}
|
||||||
i.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
if i.overrideOption != 0 {
|
||||||
metadata.Inbound = i.Tag()
|
i.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||||
metadata.InboundType = i.Type()
|
}
|
||||||
metadata.InboundDetour = i.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = i.listener.ListenOptions().InboundOptions
|
|
||||||
i.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
i.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +120,9 @@ func (i *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = i.Tag()
|
metadata.Inbound = i.Tag()
|
||||||
metadata.InboundType = i.Type()
|
metadata.InboundType = i.Type()
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundDetour = i.listener.ListenOptions().Detour
|
metadata.InboundDetour = i.listener.ListenOptions().Detour
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = i.listener.ListenOptions().InboundOptions
|
metadata.InboundOptions = i.listener.ListenOptions().InboundOptions
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
|
|||||||
@@ -63,9 +63,11 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
dialer: outboundDialer,
|
dialer: outboundDialer,
|
||||||
// loopBack: newLoopBackDetector(router),
|
// loopBack: newLoopBackDetector(router),
|
||||||
}
|
}
|
||||||
|
//nolint:staticcheck
|
||||||
if options.ProxyProtocol != 0 {
|
if options.ProxyProtocol != 0 {
|
||||||
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
|
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
|
||||||
}
|
}
|
||||||
|
//nolint:staticcheck
|
||||||
if options.OverrideAddress != "" && options.OverridePort != 0 {
|
if options.OverrideAddress != "" && options.OverridePort != 0 {
|
||||||
outbound.overrideOption = 1
|
outbound.overrideOption = 1
|
||||||
outbound.overrideDestination = M.ParseSocksaddrHostPort(options.OverrideAddress, options.OverridePort)
|
outbound.overrideDestination = M.ParseSocksaddrHostPort(options.OverrideAddress, options.OverridePort)
|
||||||
@@ -161,6 +163,7 @@ func (h *Outbound) DialParallel(ctx context.Context, network string, destination
|
|||||||
if h.domainStrategy != dns.DomainStrategyAsIS {
|
if h.domainStrategy != dns.DomainStrategyAsIS {
|
||||||
domainStrategy = h.domainStrategy
|
domainStrategy = h.domainStrategy
|
||||||
} else {
|
} else {
|
||||||
|
//nolint:staticcheck
|
||||||
domainStrategy = dns.DomainStrategy(metadata.InboundOptions.DomainStrategy)
|
domainStrategy = dns.DomainStrategy(metadata.InboundOptions.DomainStrategy)
|
||||||
}
|
}
|
||||||
switch domainStrategy {
|
switch domainStrategy {
|
||||||
@@ -200,6 +203,7 @@ func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, dest
|
|||||||
if h.domainStrategy != dns.DomainStrategyAsIS {
|
if h.domainStrategy != dns.DomainStrategyAsIS {
|
||||||
domainStrategy = h.domainStrategy
|
domainStrategy = h.domainStrategy
|
||||||
} else {
|
} else {
|
||||||
|
//nolint:staticcheck
|
||||||
domainStrategy = dns.DomainStrategy(metadata.InboundOptions.DomainStrategy)
|
domainStrategy = dns.DomainStrategy(metadata.InboundOptions.DomainStrategy)
|
||||||
}
|
}
|
||||||
switch domainStrategy {
|
switch domainStrategy {
|
||||||
|
|||||||
@@ -42,20 +42,21 @@ func (d *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
|
|||||||
return nil, os.ErrInvalid
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated
|
func (d *Outbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
func (d *Outbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
|
||||||
metadata.Destination = M.Socksaddr{}
|
metadata.Destination = M.Socksaddr{}
|
||||||
defer conn.Close()
|
|
||||||
for {
|
for {
|
||||||
conn.SetReadDeadline(time.Now().Add(C.DNSTimeout))
|
conn.SetReadDeadline(time.Now().Add(C.DNSTimeout))
|
||||||
err := HandleStreamDNSRequest(ctx, d.router, conn, metadata)
|
err := HandleStreamDNSRequest(ctx, d.router, conn, metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
conn.Close()
|
||||||
|
if onClose != nil {
|
||||||
|
onClose(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated
|
func (d *Outbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
func (d *Outbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
NewDNSPacketConnection(ctx, d.router, conn, nil, metadata)
|
||||||
return NewDNSPacketConnection(ctx, d.router, conn, nil, metadata)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing/common/atomic"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
@@ -21,17 +22,22 @@ func RegisterSelector(registry *outbound.Registry) {
|
|||||||
outbound.Register[option.SelectorOutboundOptions](registry, C.TypeSelector, NewSelector)
|
outbound.Register[option.SelectorOutboundOptions](registry, C.TypeSelector, NewSelector)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ adapter.OutboundGroup = (*Selector)(nil)
|
var (
|
||||||
|
_ adapter.OutboundGroup = (*Selector)(nil)
|
||||||
|
_ adapter.ConnectionHandlerEx = (*Selector)(nil)
|
||||||
|
_ adapter.PacketConnectionHandlerEx = (*Selector)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
type Selector struct {
|
type Selector struct {
|
||||||
outbound.Adapter
|
outbound.Adapter
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
outboundManager adapter.OutboundManager
|
outbound adapter.OutboundManager
|
||||||
|
connection adapter.ConnectionManager
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
tags []string
|
tags []string
|
||||||
defaultTag string
|
defaultTag string
|
||||||
outbounds map[string]adapter.Outbound
|
outbounds map[string]adapter.Outbound
|
||||||
selected adapter.Outbound
|
selected atomic.TypedValue[adapter.Outbound]
|
||||||
interruptGroup *interrupt.Group
|
interruptGroup *interrupt.Group
|
||||||
interruptExternalConnections bool
|
interruptExternalConnections bool
|
||||||
}
|
}
|
||||||
@@ -40,7 +46,8 @@ func NewSelector(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
outbound := &Selector{
|
outbound := &Selector{
|
||||||
Adapter: outbound.NewAdapter(C.TypeSelector, tag, nil, options.Outbounds),
|
Adapter: outbound.NewAdapter(C.TypeSelector, tag, nil, options.Outbounds),
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
outboundManager: service.FromContext[adapter.OutboundManager](ctx),
|
outbound: service.FromContext[adapter.OutboundManager](ctx),
|
||||||
|
connection: service.FromContext[adapter.ConnectionManager](ctx),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
tags: options.Outbounds,
|
tags: options.Outbounds,
|
||||||
defaultTag: options.Default,
|
defaultTag: options.Default,
|
||||||
@@ -55,15 +62,16 @@ func NewSelector(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Selector) Network() []string {
|
func (s *Selector) Network() []string {
|
||||||
if s.selected == nil {
|
selected := s.selected.Load()
|
||||||
|
if selected == nil {
|
||||||
return []string{N.NetworkTCP, N.NetworkUDP}
|
return []string{N.NetworkTCP, N.NetworkUDP}
|
||||||
}
|
}
|
||||||
return s.selected.Network()
|
return selected.Network()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Selector) Start() error {
|
func (s *Selector) Start() error {
|
||||||
for i, tag := range s.tags {
|
for i, tag := range s.tags {
|
||||||
detour, loaded := s.outboundManager.Outbound(tag)
|
detour, loaded := s.outbound.Outbound(tag)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return E.New("outbound ", i, " not found: ", tag)
|
return E.New("outbound ", i, " not found: ", tag)
|
||||||
}
|
}
|
||||||
@@ -77,7 +85,7 @@ func (s *Selector) Start() error {
|
|||||||
if selected != "" {
|
if selected != "" {
|
||||||
detour, loaded := s.outbounds[selected]
|
detour, loaded := s.outbounds[selected]
|
||||||
if loaded {
|
if loaded {
|
||||||
s.selected = detour
|
s.selected.Store(detour)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,16 +97,16 @@ func (s *Selector) Start() error {
|
|||||||
if !loaded {
|
if !loaded {
|
||||||
return E.New("default outbound not found: ", s.defaultTag)
|
return E.New("default outbound not found: ", s.defaultTag)
|
||||||
}
|
}
|
||||||
s.selected = detour
|
s.selected.Store(detour)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
s.selected = s.outbounds[s.tags[0]]
|
s.selected.Store(s.outbounds[s.tags[0]])
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Selector) Now() string {
|
func (s *Selector) Now() string {
|
||||||
selected := s.selected
|
selected := s.selected.Load()
|
||||||
if selected == nil {
|
if selected == nil {
|
||||||
return s.tags[0]
|
return s.tags[0]
|
||||||
}
|
}
|
||||||
@@ -114,10 +122,9 @@ func (s *Selector) SelectOutbound(tag string) bool {
|
|||||||
if !loaded {
|
if !loaded {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if s.selected == detour {
|
if s.selected.Swap(detour) == detour {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
s.selected = detour
|
|
||||||
if s.Tag() != "" {
|
if s.Tag() != "" {
|
||||||
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
||||||
if cacheFile != nil {
|
if cacheFile != nil {
|
||||||
@@ -132,7 +139,7 @@ func (s *Selector) SelectOutbound(tag string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Selector) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (s *Selector) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
conn, err := s.selected.DialContext(ctx, network, destination)
|
conn, err := s.selected.Load().DialContext(ctx, network, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -140,32 +147,30 @@ func (s *Selector) DialContext(ctx context.Context, network string, destination
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Selector) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (s *Selector) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
conn, err := s.selected.ListenPacket(ctx, destination)
|
conn, err := s.selected.Load().ListenPacket(ctx, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return s.interruptGroup.NewPacketConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
|
return s.interruptGroup.NewPacketConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
func (s *Selector) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
// Deprecated
|
|
||||||
func (s *Selector) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
|
||||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||||
if legacyHandler, ok := s.selected.(adapter.ConnectionHandler); ok {
|
selected := s.selected.Load()
|
||||||
return legacyHandler.NewConnection(ctx, conn, metadata)
|
if outboundHandler, isHandler := selected.(adapter.ConnectionHandlerEx); isHandler {
|
||||||
|
outboundHandler.NewConnectionEx(ctx, conn, metadata, onClose)
|
||||||
} else {
|
} else {
|
||||||
return outbound.NewConnection(ctx, s.selected, conn, metadata)
|
s.connection.NewConnection(ctx, selected, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
func (s *Selector) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
// Deprecated
|
|
||||||
func (s *Selector) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
|
||||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||||
if legacyHandler, ok := s.selected.(adapter.PacketConnectionHandler); ok {
|
selected := s.selected.Load()
|
||||||
return legacyHandler.NewPacketConnection(ctx, conn, metadata)
|
if outboundHandler, isHandler := selected.(adapter.PacketConnectionHandlerEx); isHandler {
|
||||||
|
outboundHandler.NewPacketConnectionEx(ctx, conn, metadata, onClose)
|
||||||
} else {
|
} else {
|
||||||
return outbound.NewPacketConnection(ctx, s.selected, conn, metadata)
|
s.connection.NewPacketConnection(ctx, selected, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ type URLTest struct {
|
|||||||
outbound.Adapter
|
outbound.Adapter
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
router adapter.Router
|
router adapter.Router
|
||||||
outboundManager adapter.OutboundManager
|
outbound adapter.OutboundManager
|
||||||
|
connection adapter.ConnectionManager
|
||||||
logger log.ContextLogger
|
logger log.ContextLogger
|
||||||
tags []string
|
tags []string
|
||||||
link string
|
link string
|
||||||
@@ -52,7 +53,8 @@ func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
Adapter: outbound.NewAdapter(C.TypeURLTest, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.Outbounds),
|
Adapter: outbound.NewAdapter(C.TypeURLTest, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.Outbounds),
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
router: router,
|
router: router,
|
||||||
outboundManager: service.FromContext[adapter.OutboundManager](ctx),
|
outbound: service.FromContext[adapter.OutboundManager](ctx),
|
||||||
|
connection: service.FromContext[adapter.ConnectionManager](ctx),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
tags: options.Outbounds,
|
tags: options.Outbounds,
|
||||||
link: options.URL,
|
link: options.URL,
|
||||||
@@ -70,13 +72,13 @@ func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
func (s *URLTest) Start() error {
|
func (s *URLTest) Start() error {
|
||||||
outbounds := make([]adapter.Outbound, 0, len(s.tags))
|
outbounds := make([]adapter.Outbound, 0, len(s.tags))
|
||||||
for i, tag := range s.tags {
|
for i, tag := range s.tags {
|
||||||
detour, loaded := s.outboundManager.Outbound(tag)
|
detour, loaded := s.outbound.Outbound(tag)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return E.New("outbound ", i, " not found: ", tag)
|
return E.New("outbound ", i, " not found: ", tag)
|
||||||
}
|
}
|
||||||
outbounds = append(outbounds, detour)
|
outbounds = append(outbounds, detour)
|
||||||
}
|
}
|
||||||
group, err := NewURLTestGroup(s.ctx, s.outboundManager, s.logger, outbounds, s.link, s.interval, s.tolerance, s.idleTimeout, s.interruptExternalConnections)
|
group, err := NewURLTestGroup(s.ctx, s.outbound, s.logger, outbounds, s.link, s.interval, s.tolerance, s.idleTimeout, s.interruptExternalConnections)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -160,18 +162,14 @@ func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (ne
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
func (s *URLTest) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
// Deprecated
|
|
||||||
func (s *URLTest) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
|
||||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||||
return outbound.NewConnection(ctx, s, conn, metadata)
|
s.connection.NewConnection(ctx, s, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
func (s *URLTest) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
// Deprecated
|
|
||||||
func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
|
||||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||||
return outbound.NewPacketConnection(ctx, s, conn, metadata)
|
s.connection.NewPacketConnection(ctx, s, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *URLTest) InterfaceUpdated() {
|
func (s *URLTest) InterfaceUpdated() {
|
||||||
|
|||||||
@@ -82,33 +82,25 @@ func (h *Inbound) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
err := h.newConnection(ctx, conn, metadata, onClose)
|
|
||||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
|
||||||
if err != nil {
|
|
||||||
if E.IsClosedOrCanceled(err) {
|
|
||||||
h.logger.DebugContext(ctx, "connection closed: ", err)
|
|
||||||
} else {
|
|
||||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
|
|
||||||
var err error
|
var err error
|
||||||
if h.tlsConfig != nil {
|
if h.tlsConfig != nil {
|
||||||
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||||
|
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source, ": TLS handshake"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return http.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
err = http.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
||||||
|
if err != nil {
|
||||||
|
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||||
|
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
user, loaded := auth.UserFromContext[string](ctx)
|
user, loaded := auth.UserFromContext[string](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||||
@@ -123,8 +115,6 @@ func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata
|
|||||||
func (h *Inbound) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (h *Inbound) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
user, loaded := auth.UserFromContext[string](ctx)
|
user, loaded := auth.UserFromContext[string](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
}
|
}
|
||||||
if len(options.Down) > 0 {
|
if len(options.Down) > 0 {
|
||||||
receiveBps, err = humanize.ParseBytes(options.Down)
|
receiveBps, err = humanize.ParseBytes(options.Down)
|
||||||
if receiveBps == 0 {
|
if err != nil {
|
||||||
return nil, E.New("invalid down speed format: ", options.Down)
|
return nil, E.New("invalid down speed format: ", options.Down)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -123,7 +123,9 @@ func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.S
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||||
metadata.OriginDestination = h.listener.UDPAddr()
|
metadata.OriginDestination = h.listener.UDPAddr()
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
@@ -144,7 +146,9 @@ func (h *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||||
metadata.OriginDestination = h.listener.UDPAddr()
|
metadata.OriginDestination = h.listener.UDPAddr()
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
}
|
}
|
||||||
if len(options.Down) > 0 {
|
if len(options.Down) > 0 {
|
||||||
receiveBps, err = humanize.ParseBytes(options.Down)
|
receiveBps, err = humanize.ParseBytes(options.Down)
|
||||||
if receiveBps == 0 {
|
if err != nil {
|
||||||
return nil, E.New("invalid down speed format: ", options.Down)
|
return nil, E.Cause(err, "invalid down speed format: ", options.Down)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps
|
receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps
|
||||||
|
|||||||
@@ -60,26 +60,40 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var masqueradeHandler http.Handler
|
var masqueradeHandler http.Handler
|
||||||
if options.Masquerade != "" {
|
if options.Masquerade != nil && options.Masquerade.Type != "" {
|
||||||
masqueradeURL, err := url.Parse(options.Masquerade)
|
switch options.Masquerade.Type {
|
||||||
if err != nil {
|
case C.Hysterai2MasqueradeTypeFile:
|
||||||
return nil, E.Cause(err, "parse masquerade URL")
|
masqueradeHandler = http.FileServer(http.Dir(options.Masquerade.FileOptions.Directory))
|
||||||
}
|
case C.Hysterai2MasqueradeTypeProxy:
|
||||||
switch masqueradeURL.Scheme {
|
masqueradeURL, err := url.Parse(options.Masquerade.ProxyOptions.URL)
|
||||||
case "file":
|
if err != nil {
|
||||||
masqueradeHandler = http.FileServer(http.Dir(masqueradeURL.Path))
|
return nil, E.Cause(err, "parse masquerade URL")
|
||||||
case "http", "https":
|
}
|
||||||
masqueradeHandler = &httputil.ReverseProxy{
|
masqueradeHandler = &httputil.ReverseProxy{
|
||||||
Rewrite: func(r *httputil.ProxyRequest) {
|
Rewrite: func(r *httputil.ProxyRequest) {
|
||||||
r.SetURL(masqueradeURL)
|
r.SetURL(masqueradeURL)
|
||||||
r.Out.Host = r.In.Host
|
if !options.Masquerade.ProxyOptions.RewriteHost {
|
||||||
|
r.Out.Host = r.In.Host
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||||
w.WriteHeader(http.StatusBadGateway)
|
w.WriteHeader(http.StatusBadGateway)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
case C.Hysterai2MasqueradeTypeString:
|
||||||
|
masqueradeHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if options.Masquerade.StringOptions.StatusCode != 0 {
|
||||||
|
w.WriteHeader(options.Masquerade.StringOptions.StatusCode)
|
||||||
|
}
|
||||||
|
for key, values := range options.Masquerade.StringOptions.Headers {
|
||||||
|
for _, value := range values {
|
||||||
|
w.Header().Add(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.Write([]byte(options.Masquerade.StringOptions.Content))
|
||||||
|
})
|
||||||
default:
|
default:
|
||||||
return nil, E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme)
|
return nil, E.New("unknown masquerade type: ", options.Masquerade.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inbound := &Inbound{
|
inbound := &Inbound{
|
||||||
@@ -134,7 +148,9 @@ func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.S
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||||
metadata.OriginDestination = h.listener.UDPAddr()
|
metadata.OriginDestination = h.listener.UDPAddr()
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
@@ -155,7 +171,9 @@ func (h *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||||
metadata.OriginDestination = h.listener.UDPAddr()
|
metadata.OriginDestination = h.listener.UDPAddr()
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
|
|||||||
@@ -85,17 +85,15 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
|
|||||||
}
|
}
|
||||||
switch headerBytes[0] {
|
switch headerBytes[0] {
|
||||||
case socks4.Version, socks5.Version:
|
case socks4.Version, socks5.Version:
|
||||||
return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, metadata.Destination, onClose)
|
return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
||||||
default:
|
default:
|
||||||
return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
user, loaded := auth.UserFromContext[string](ctx)
|
user, loaded := auth.UserFromContext[string](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||||
@@ -110,8 +108,6 @@ func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata
|
|||||||
func (h *Inbound) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (h *Inbound) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
user, loaded := auth.UserFromContext[string](ctx)
|
user, loaded := auth.UserFromContext[string](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||||
|
|||||||
@@ -195,7 +195,9 @@ func (n *Inbound) newConnection(ctx context.Context, waitForClose bool, conn net
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = n.Tag()
|
metadata.Inbound = n.Tag()
|
||||||
metadata.InboundType = n.Type()
|
metadata.InboundType = n.Type()
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundDetour = n.listener.ListenOptions().Detour
|
metadata.InboundDetour = n.listener.ListenOptions().Detour
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = n.listener.ListenOptions().InboundOptions
|
metadata.InboundOptions = n.listener.ListenOptions().InboundOptions
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
|
|||||||
@@ -62,8 +62,6 @@ func (h *Redirect) NewConnectionEx(ctx context.Context, conn net.Conn, metadata
|
|||||||
}
|
}
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
metadata.Destination = M.SocksaddrFromNetIP(destination)
|
metadata.Destination = M.SocksaddrFromNetIP(destination)
|
||||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||||
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||||
|
|||||||
@@ -93,6 +93,8 @@ func (t *TProxy) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *TProxy) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (t *TProxy) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
|
metadata.Inbound = t.Tag()
|
||||||
|
metadata.InboundType = t.Type()
|
||||||
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||||
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||||
t.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
t.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||||
@@ -104,8 +106,6 @@ func (t *TProxy) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, s
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = t.Tag()
|
metadata.Inbound = t.Tag()
|
||||||
metadata.InboundType = t.Type()
|
metadata.InboundType = t.Type()
|
||||||
metadata.InboundDetour = t.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = t.listener.ListenOptions().InboundOptions
|
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
metadata.OriginDestination = t.listener.UDPAddr()
|
metadata.OriginDestination = t.listener.UDPAddr()
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ func (h *Inbound) Close() error {
|
|||||||
return h.listener.Close()
|
return h.listener.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:staticcheck
|
||||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
||||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||||
@@ -116,6 +117,7 @@ func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:staticcheck
|
||||||
func (h *Inbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
func (h *Inbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||||
err := h.service.NewPacket(h.ctx, &stubPacketConn{h.listener.PacketWriter()}, buffer, M.Metadata{Source: source})
|
err := h.service.NewPacket(h.ctx, &stubPacketConn{h.listener.PacketWriter()}, buffer, M.Metadata{Source: source})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -127,8 +129,6 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
|
|||||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
return h.router.RouteConnection(ctx, conn, metadata)
|
return h.router.RouteConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,8 +138,6 @@ func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, me
|
|||||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ func (h *MultiInbound) Close() error {
|
|||||||
return h.listener.Close()
|
return h.listener.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:staticcheck
|
||||||
func (h *MultiInbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (h *MultiInbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
||||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||||
@@ -124,6 +125,7 @@ func (h *MultiInbound) NewConnectionEx(ctx context.Context, conn net.Conn, metad
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:staticcheck
|
||||||
func (h *MultiInbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
func (h *MultiInbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||||
err := h.service.NewPacket(h.ctx, &stubPacketConn{h.listener.PacketWriter()}, buffer, M.Metadata{Source: source})
|
err := h.service.NewPacket(h.ctx, &stubPacketConn{h.listener.PacketWriter()}, buffer, M.Metadata{Source: source})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -145,7 +147,9 @@ func (h *MultiInbound) newConnection(ctx context.Context, conn net.Conn, metadat
|
|||||||
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||||
return h.router.RouteConnection(ctx, conn, metadata)
|
return h.router.RouteConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
@@ -166,11 +170,14 @@ func (h *MultiInbound) newPacketConnection(ctx context.Context, conn N.PacketCon
|
|||||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:staticcheck
|
||||||
func (h *MultiInbound) NewError(ctx context.Context, err error) {
|
func (h *MultiInbound) NewError(ctx context.Context, err error) {
|
||||||
NewError(h.logger, ctx, err)
|
NewError(h.logger, ctx, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ func (h *RelayInbound) Close() error {
|
|||||||
return h.listener.Close()
|
return h.listener.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:staticcheck
|
||||||
func (h *RelayInbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (h *RelayInbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
|
||||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||||
@@ -109,6 +110,7 @@ func (h *RelayInbound) NewConnectionEx(ctx context.Context, conn net.Conn, metad
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:staticcheck
|
||||||
func (h *RelayInbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
func (h *RelayInbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||||
err := h.service.NewPacket(h.ctx, &stubPacketConn{h.listener.PacketWriter()}, buffer, M.Metadata{Source: source})
|
err := h.service.NewPacket(h.ctx, &stubPacketConn{h.listener.PacketWriter()}, buffer, M.Metadata{Source: source})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -130,7 +132,9 @@ func (h *RelayInbound) newConnection(ctx context.Context, conn net.Conn, metadat
|
|||||||
h.logger.InfoContext(ctx, "[", destination, "] inbound connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "[", destination, "] inbound connection to ", metadata.Destination)
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||||
return h.router.RouteConnection(ctx, conn, metadata)
|
return h.router.RouteConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
@@ -151,11 +155,14 @@ func (h *RelayInbound) newPacketConnection(ctx context.Context, conn N.PacketCon
|
|||||||
h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection to ", metadata.Destination)
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:staticcheck
|
||||||
func (h *RelayInbound) NewError(ctx context.Context, err error) {
|
func (h *RelayInbound) NewError(ctx context.Context, err error) {
|
||||||
NewError(h.logger, ctx, err)
|
NewError(h.logger, ctx, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,7 +119,9 @@ func (h *inboundHandler) NewConnectionEx(ctx context.Context, conn net.Conn, sou
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func (h *Inbound) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, metadata.Destination, onClose)
|
err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
||||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if E.IsClosedOrCanceled(err) {
|
if E.IsClosedOrCanceled(err) {
|
||||||
@@ -76,8 +76,6 @@ func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata a
|
|||||||
func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
user, loaded := auth.UserFromContext[string](ctx)
|
user, loaded := auth.UserFromContext[string](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||||
@@ -92,8 +90,6 @@ func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata
|
|||||||
func (h *Inbound) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (h *Inbound) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
user, loaded := auth.UserFromContext[string](ctx)
|
user, loaded := auth.UserFromContext[string](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package tor
|
package tor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
std_bufio "bufio"
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/adapter/outbound"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/auth"
|
"github.com/sagernet/sing/common/auth"
|
||||||
@@ -15,12 +15,14 @@ import (
|
|||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/protocol/socks"
|
"github.com/sagernet/sing/protocol/socks"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProxyListener struct {
|
type ProxyListener struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
logger log.ContextLogger
|
logger log.ContextLogger
|
||||||
dialer N.Dialer
|
dialer N.Dialer
|
||||||
|
connection adapter.ConnectionManager
|
||||||
tcpListener *net.TCPListener
|
tcpListener *net.TCPListener
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
@@ -38,6 +40,7 @@ func NewProxyListener(ctx context.Context, logger log.ContextLogger, dialer N.Di
|
|||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
dialer: dialer,
|
dialer: dialer,
|
||||||
|
connection: service.FromContext[adapter.ConnectionManager](ctx),
|
||||||
authenticator: auth.NewAuthenticator([]auth.User{{Username: username, Password: password}}),
|
authenticator: auth.NewAuthenticator([]auth.User{{Username: username, Password: password}}),
|
||||||
username: username,
|
username: username,
|
||||||
password: password,
|
password: password,
|
||||||
@@ -95,25 +98,24 @@ func (l *ProxyListener) acceptLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: migrate to new api
|
|
||||||
//
|
|
||||||
//nolint:staticcheck
|
|
||||||
func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error {
|
func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error {
|
||||||
return socks.HandleConnection(ctx, conn, l.authenticator, l, M.Metadata{})
|
return socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), l.authenticator, l, M.SocksaddrFromNet(conn.RemoteAddr()), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *ProxyListener) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata M.Metadata) error {
|
func (l *ProxyListener) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
|
metadata.Source = source
|
||||||
|
metadata.Destination = destination
|
||||||
metadata.Network = N.NetworkTCP
|
metadata.Network = N.NetworkTCP
|
||||||
metadata.Destination = upstreamMetadata.Destination
|
|
||||||
l.logger.InfoContext(ctx, "proxy connection to ", metadata.Destination)
|
l.logger.InfoContext(ctx, "proxy connection to ", metadata.Destination)
|
||||||
return outbound.NewConnection(ctx, l.dialer, conn, metadata)
|
l.connection.NewConnection(ctx, l.dialer, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *ProxyListener) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstreamMetadata M.Metadata) error {
|
func (l *ProxyListener) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
|
metadata.Source = source
|
||||||
|
metadata.Destination = destination
|
||||||
metadata.Network = N.NetworkUDP
|
metadata.Network = N.NetworkUDP
|
||||||
metadata.Destination = upstreamMetadata.Destination
|
|
||||||
l.logger.InfoContext(ctx, "proxy packet connection to ", metadata.Destination)
|
l.logger.InfoContext(ctx, "proxy packet connection to ", metadata.Destination)
|
||||||
return outbound.NewPacketConnection(ctx, l.dialer, conn, metadata)
|
l.connection.NewPacketConnection(ctx, l.dialer, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
}
|
}
|
||||||
inbound.tlsConfig = tlsConfig
|
inbound.tlsConfig = tlsConfig
|
||||||
}
|
}
|
||||||
var fallbackHandler N.TCPConnectionHandler
|
var fallbackHandler N.TCPConnectionHandlerEx
|
||||||
if options.Fallback != nil && options.Fallback.Server != "" || len(options.FallbackForALPN) > 0 {
|
if options.Fallback != nil && options.Fallback.Server != "" || len(options.FallbackForALPN) > 0 {
|
||||||
if options.Fallback != nil && options.Fallback.Server != "" {
|
if options.Fallback != nil && options.Fallback.Server != "" {
|
||||||
inbound.fallbackAddr = options.Fallback.Build()
|
inbound.fallbackAddr = options.Fallback.Build()
|
||||||
@@ -78,9 +78,9 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
}
|
}
|
||||||
inbound.fallbackAddrTLSNextProto = fallbackAddrNextProto
|
inbound.fallbackAddrTLSNextProto = fallbackAddrNextProto
|
||||||
}
|
}
|
||||||
fallbackHandler = adapter.NewUpstreamContextHandler(inbound.fallbackConnection, nil, nil)
|
fallbackHandler = adapter.NewUpstreamContextHandlerEx(inbound.fallbackConnection, nil)
|
||||||
}
|
}
|
||||||
service := trojan.NewService[int](adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, nil), fallbackHandler, logger)
|
service := trojan.NewService[int](adapter.NewUpstreamContextHandlerEx(inbound.newConnection, inbound.newPacketConnection), fallbackHandler, logger)
|
||||||
err := service.UpdateUsers(common.MapIndexed(options.Users, func(index int, it option.TrojanUser) int {
|
err := service.UpdateUsers(common.MapIndexed(options.Users, func(index int, it option.TrojanUser) int {
|
||||||
return index
|
return index
|
||||||
}), common.Map(options.Users, func(it option.TrojanUser) string {
|
}), common.Map(options.Users, func(it option.TrojanUser) string {
|
||||||
@@ -158,37 +158,30 @@ func (h *Inbound) Close() error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
var err error
|
var err error
|
||||||
if h.tlsConfig != nil && h.transport == nil {
|
if h.tlsConfig != nil && h.transport == nil {
|
||||||
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||||
|
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source, ": TLS handshake"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, adapter.UpstreamMetadata(metadata))
|
err = h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Source, onClose)
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
|
||||||
err := h.NewConnection(ctx, conn, metadata)
|
|
||||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if E.IsClosedOrCanceled(err) {
|
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||||
h.logger.DebugContext(ctx, "connection closed: ", err)
|
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||||
} else {
|
|
||||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return os.ErrInvalid
|
N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
user := h.users[userIndex].Name
|
user := h.users[userIndex].Name
|
||||||
if user == "" {
|
if user == "" {
|
||||||
@@ -197,44 +190,16 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
|
|||||||
metadata.User = user
|
metadata.User = user
|
||||||
}
|
}
|
||||||
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
||||||
return h.router.RouteConnection(ctx, conn, metadata)
|
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) fallbackConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
var fallbackAddr M.Socksaddr
|
|
||||||
if len(h.fallbackAddrTLSNextProto) > 0 {
|
|
||||||
if tlsConn, loaded := common.Cast[tls.Conn](conn); loaded {
|
|
||||||
connectionState := tlsConn.ConnectionState()
|
|
||||||
if connectionState.NegotiatedProtocol != "" {
|
|
||||||
if fallbackAddr, loaded = h.fallbackAddrTLSNextProto[connectionState.NegotiatedProtocol]; !loaded {
|
|
||||||
return E.New("fallback disabled for ALPN: ", connectionState.NegotiatedProtocol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !fallbackAddr.IsValid() {
|
|
||||||
if !h.fallbackAddr.IsValid() {
|
|
||||||
return E.New("fallback disabled by default")
|
|
||||||
}
|
|
||||||
fallbackAddr = h.fallbackAddr
|
|
||||||
}
|
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
h.logger.InfoContext(ctx, "fallback connection to ", fallbackAddr)
|
|
||||||
metadata.Destination = fallbackAddr
|
|
||||||
return h.router.RouteConnection(ctx, conn, metadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
|
||||||
metadata.Inbound = h.Tag()
|
|
||||||
metadata.InboundType = h.Type()
|
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return os.ErrInvalid
|
N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
user := h.users[userIndex].Name
|
user := h.users[userIndex].Name
|
||||||
if user == "" {
|
if user == "" {
|
||||||
@@ -243,7 +208,36 @@ func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, me
|
|||||||
metadata.User = user
|
metadata.User = user
|
||||||
}
|
}
|
||||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Inbound) fallbackConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
|
var fallbackAddr M.Socksaddr
|
||||||
|
if len(h.fallbackAddrTLSNextProto) > 0 {
|
||||||
|
if tlsConn, loaded := common.Cast[tls.Conn](conn); loaded {
|
||||||
|
connectionState := tlsConn.ConnectionState()
|
||||||
|
if connectionState.NegotiatedProtocol != "" {
|
||||||
|
if fallbackAddr, loaded = h.fallbackAddrTLSNextProto[connectionState.NegotiatedProtocol]; !loaded {
|
||||||
|
h.logger.DebugContext(ctx, "process connection from ", metadata.Source, ": fallback disabled for ALPN: ", connectionState.NegotiatedProtocol)
|
||||||
|
N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !fallbackAddr.IsValid() {
|
||||||
|
if !h.fallbackAddr.IsValid() {
|
||||||
|
h.logger.DebugContext(ctx, "process connection from ", metadata.Source, ": fallback disabled by default")
|
||||||
|
N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fallbackAddr = h.fallbackAddr
|
||||||
|
}
|
||||||
|
metadata.Inbound = h.Tag()
|
||||||
|
metadata.InboundType = h.Type()
|
||||||
|
metadata.Destination = fallbackAddr
|
||||||
|
h.logger.InfoContext(ctx, "fallback connection to ", fallbackAddr)
|
||||||
|
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ adapter.V2RayServerTransportHandler = (*inboundTransportHandler)(nil)
|
var _ adapter.V2RayServerTransportHandler = (*inboundTransportHandler)(nil)
|
||||||
@@ -254,6 +248,10 @@ func (h *inboundTransportHandler) NewConnectionEx(ctx context.Context, conn net.
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
|
//nolint:staticcheck
|
||||||
|
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||||
|
//nolint:staticcheck
|
||||||
|
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||||
(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
|
(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,9 @@ func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.S
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||||
metadata.OriginDestination = h.listener.UDPAddr()
|
metadata.OriginDestination = h.listener.UDPAddr()
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
@@ -126,7 +128,9 @@ func (h *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||||
metadata.OriginDestination = h.listener.UDPAddr()
|
metadata.OriginDestination = h.listener.UDPAddr()
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
|
|||||||
@@ -41,11 +41,9 @@ type Inbound struct {
|
|||||||
router adapter.Router
|
router adapter.Router
|
||||||
networkManager adapter.NetworkManager
|
networkManager adapter.NetworkManager
|
||||||
logger log.ContextLogger
|
logger log.ContextLogger
|
||||||
// Deprecated
|
//nolint:staticcheck
|
||||||
inboundOptions option.InboundOptions
|
inboundOptions option.InboundOptions
|
||||||
tunOptions tun.Options
|
tunOptions tun.Options
|
||||||
// Deprecated
|
|
||||||
endpointIndependentNat bool
|
|
||||||
udpTimeout time.Duration
|
udpTimeout time.Duration
|
||||||
stack string
|
stack string
|
||||||
tunIf tun.Tun
|
tunIf tun.Tun
|
||||||
@@ -64,14 +62,14 @@ type Inbound struct {
|
|||||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (adapter.Inbound, error) {
|
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (adapter.Inbound, error) {
|
||||||
address := options.Address
|
address := options.Address
|
||||||
var deprecatedAddressUsed bool
|
var deprecatedAddressUsed bool
|
||||||
|
|
||||||
//nolint:staticcheck
|
//nolint:staticcheck
|
||||||
//goland:noinspection GoDeprecation
|
|
||||||
if len(options.Inet4Address) > 0 {
|
if len(options.Inet4Address) > 0 {
|
||||||
address = append(address, options.Inet4Address...)
|
address = append(address, options.Inet4Address...)
|
||||||
deprecatedAddressUsed = true
|
deprecatedAddressUsed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:staticcheck
|
//nolint:staticcheck
|
||||||
//goland:noinspection GoDeprecation
|
|
||||||
if len(options.Inet6Address) > 0 {
|
if len(options.Inet6Address) > 0 {
|
||||||
address = append(address, options.Inet6Address...)
|
address = append(address, options.Inet6Address...)
|
||||||
deprecatedAddressUsed = true
|
deprecatedAddressUsed = true
|
||||||
@@ -84,14 +82,14 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
})
|
})
|
||||||
|
|
||||||
routeAddress := options.RouteAddress
|
routeAddress := options.RouteAddress
|
||||||
|
|
||||||
//nolint:staticcheck
|
//nolint:staticcheck
|
||||||
//goland:noinspection GoDeprecation
|
|
||||||
if len(options.Inet4RouteAddress) > 0 {
|
if len(options.Inet4RouteAddress) > 0 {
|
||||||
routeAddress = append(routeAddress, options.Inet4RouteAddress...)
|
routeAddress = append(routeAddress, options.Inet4RouteAddress...)
|
||||||
deprecatedAddressUsed = true
|
deprecatedAddressUsed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:staticcheck
|
//nolint:staticcheck
|
||||||
//goland:noinspection GoDeprecation
|
|
||||||
if len(options.Inet6RouteAddress) > 0 {
|
if len(options.Inet6RouteAddress) > 0 {
|
||||||
routeAddress = append(routeAddress, options.Inet6RouteAddress...)
|
routeAddress = append(routeAddress, options.Inet6RouteAddress...)
|
||||||
deprecatedAddressUsed = true
|
deprecatedAddressUsed = true
|
||||||
@@ -104,14 +102,14 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
})
|
})
|
||||||
|
|
||||||
routeExcludeAddress := options.RouteExcludeAddress
|
routeExcludeAddress := options.RouteExcludeAddress
|
||||||
|
|
||||||
//nolint:staticcheck
|
//nolint:staticcheck
|
||||||
//goland:noinspection GoDeprecation
|
|
||||||
if len(options.Inet4RouteExcludeAddress) > 0 {
|
if len(options.Inet4RouteExcludeAddress) > 0 {
|
||||||
routeExcludeAddress = append(routeExcludeAddress, options.Inet4RouteExcludeAddress...)
|
routeExcludeAddress = append(routeExcludeAddress, options.Inet4RouteExcludeAddress...)
|
||||||
deprecatedAddressUsed = true
|
deprecatedAddressUsed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:staticcheck
|
//nolint:staticcheck
|
||||||
//goland:noinspection GoDeprecation
|
|
||||||
if len(options.Inet6RouteExcludeAddress) > 0 {
|
if len(options.Inet6RouteExcludeAddress) > 0 {
|
||||||
routeExcludeAddress = append(routeExcludeAddress, options.Inet6RouteExcludeAddress...)
|
routeExcludeAddress = append(routeExcludeAddress, options.Inet6RouteExcludeAddress...)
|
||||||
deprecatedAddressUsed = true
|
deprecatedAddressUsed = true
|
||||||
@@ -127,6 +125,11 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
deprecated.Report(ctx, deprecated.OptionTUNAddressX)
|
deprecated.Report(ctx, deprecated.OptionTUNAddressX)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:staticcheck
|
||||||
|
if options.GSO {
|
||||||
|
deprecated.Report(ctx, deprecated.OptionTUNGSO)
|
||||||
|
}
|
||||||
|
|
||||||
tunMTU := options.MTU
|
tunMTU := options.MTU
|
||||||
if tunMTU == 0 {
|
if tunMTU == 0 {
|
||||||
tunMTU = 9000
|
tunMTU = 9000
|
||||||
@@ -180,7 +183,6 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
tunOptions: tun.Options{
|
tunOptions: tun.Options{
|
||||||
Name: options.InterfaceName,
|
Name: options.InterfaceName,
|
||||||
MTU: tunMTU,
|
MTU: tunMTU,
|
||||||
GSO: options.GSO,
|
|
||||||
Inet4Address: inet4Address,
|
Inet4Address: inet4Address,
|
||||||
Inet6Address: inet6Address,
|
Inet6Address: inet6Address,
|
||||||
AutoRoute: options.AutoRoute,
|
AutoRoute: options.AutoRoute,
|
||||||
@@ -202,11 +204,10 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
ExcludePackage: options.ExcludePackage,
|
ExcludePackage: options.ExcludePackage,
|
||||||
InterfaceMonitor: networkManager.InterfaceMonitor(),
|
InterfaceMonitor: networkManager.InterfaceMonitor(),
|
||||||
},
|
},
|
||||||
endpointIndependentNat: options.EndpointIndependentNat,
|
udpTimeout: udpTimeout,
|
||||||
udpTimeout: udpTimeout,
|
stack: options.Stack,
|
||||||
stack: options.Stack,
|
platformInterface: service.FromContext[platform.Interface](ctx),
|
||||||
platformInterface: service.FromContext[platform.Interface](ctx),
|
platformOptions: common.PtrValueOrDefault(options.Platform),
|
||||||
platformOptions: common.PtrValueOrDefault(options.Platform),
|
|
||||||
}
|
}
|
||||||
if options.AutoRedirect {
|
if options.AutoRedirect {
|
||||||
if !options.AutoRoute {
|
if !options.AutoRoute {
|
||||||
@@ -436,6 +437,7 @@ func (t *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.S
|
|||||||
metadata.InboundType = C.TypeTun
|
metadata.InboundType = C.TypeTun
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = t.inboundOptions
|
metadata.InboundOptions = t.inboundOptions
|
||||||
t.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
t.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||||
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||||
@@ -449,6 +451,7 @@ func (t *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
|||||||
metadata.InboundType = C.TypeTun
|
metadata.InboundType = C.TypeTun
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = t.inboundOptions
|
metadata.InboundOptions = t.inboundOptions
|
||||||
t.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
t.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||||
t.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
t.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||||
@@ -464,6 +467,7 @@ func (t *autoRedirectHandler) NewConnectionEx(ctx context.Context, conn net.Conn
|
|||||||
metadata.InboundType = C.TypeTun
|
metadata.InboundType = C.TypeTun
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = t.inboundOptions
|
metadata.InboundOptions = t.inboundOptions
|
||||||
t.logger.InfoContext(ctx, "inbound redirect connection from ", metadata.Source)
|
t.logger.InfoContext(ctx, "inbound redirect connection from ", metadata.Source)
|
||||||
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
service := vless.NewService[int](logger, adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
service := vless.NewService[int](logger, adapter.NewUpstreamContextHandlerEx(inbound.newConnectionEx, inbound.newPacketConnectionEx))
|
||||||
service.UpdateUsers(common.MapIndexed(inbound.users, func(index int, _ option.VLESSUser) int {
|
service.UpdateUsers(common.MapIndexed(inbound.users, func(index int, _ option.VLESSUser) int {
|
||||||
return index
|
return index
|
||||||
}), common.Map(inbound.users, func(it option.VLESSUser) string {
|
}), common.Map(inbound.users, func(it option.VLESSUser) string {
|
||||||
@@ -138,37 +138,30 @@ func (h *Inbound) Close() error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
var err error
|
var err error
|
||||||
if h.tlsConfig != nil && h.transport == nil {
|
if h.tlsConfig != nil && h.transport == nil {
|
||||||
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||||
|
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source, ": TLS handshake"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
err = h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Source, onClose)
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
|
||||||
err := h.NewConnection(ctx, conn, metadata)
|
|
||||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if E.IsClosedOrCanceled(err) {
|
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||||
h.logger.DebugContext(ctx, "connection closed: ", err)
|
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||||
} else {
|
|
||||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *Inbound) newConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return os.ErrInvalid
|
N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
user := h.users[userIndex].Name
|
user := h.users[userIndex].Name
|
||||||
if user == "" {
|
if user == "" {
|
||||||
@@ -177,17 +170,16 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
|
|||||||
metadata.User = user
|
metadata.User = user
|
||||||
}
|
}
|
||||||
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
||||||
return h.router.RouteConnection(ctx, conn, metadata)
|
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (h *Inbound) newPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return os.ErrInvalid
|
N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
user := h.users[userIndex].Name
|
user := h.users[userIndex].Name
|
||||||
if user == "" {
|
if user == "" {
|
||||||
@@ -202,7 +194,7 @@ func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, me
|
|||||||
} else {
|
} else {
|
||||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||||
}
|
}
|
||||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ adapter.V2RayServerTransportHandler = (*inboundTransportHandler)(nil)
|
var _ adapter.V2RayServerTransportHandler = (*inboundTransportHandler)(nil)
|
||||||
@@ -216,17 +208,3 @@ func (h *inboundTransportHandler) NewConnectionEx(ctx context.Context, conn net.
|
|||||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||||
(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
|
(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) NewError(ctx context.Context, err error) {
|
|
||||||
NewError(h.logger, ctx, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: remove
|
|
||||||
func NewError(logger logger.ContextLogger, ctx context.Context, err error) {
|
|
||||||
common.Close(err)
|
|
||||||
if E.IsClosedOrCanceled(err) {
|
|
||||||
logger.DebugContext(ctx, "connection closed: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.ErrorContext(ctx, err)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
if options.Transport != nil && options.Transport.Type != "" {
|
if options.Transport != nil && options.Transport.Type != "" {
|
||||||
serviceOptions = append(serviceOptions, vmess.ServiceWithDisableHeaderProtection())
|
serviceOptions = append(serviceOptions, vmess.ServiceWithDisableHeaderProtection())
|
||||||
}
|
}
|
||||||
service := vmess.NewService[int](adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound), serviceOptions...)
|
service := vmess.NewService[int](adapter.NewUpstreamContextHandlerEx(inbound.newConnectionEx, inbound.newPacketConnectionEx), serviceOptions...)
|
||||||
inbound.service = service
|
inbound.service = service
|
||||||
err = service.UpdateUsers(common.MapIndexed(options.Users, func(index int, it option.VMessUser) int {
|
err = service.UpdateUsers(common.MapIndexed(options.Users, func(index int, it option.VMessUser) int {
|
||||||
return index
|
return index
|
||||||
@@ -152,37 +152,30 @@ func (h *Inbound) Close() error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
var err error
|
var err error
|
||||||
if h.tlsConfig != nil && h.transport == nil {
|
if h.tlsConfig != nil && h.transport == nil {
|
||||||
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||||
|
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source, ": TLS handshake"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
err = h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Source, onClose)
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
|
||||||
err := h.NewConnection(ctx, conn, metadata)
|
|
||||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if E.IsClosedOrCanceled(err) {
|
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||||
h.logger.DebugContext(ctx, "connection closed: ", err)
|
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||||
} else {
|
|
||||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *Inbound) newConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return os.ErrInvalid
|
N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
user := h.users[userIndex].Name
|
user := h.users[userIndex].Name
|
||||||
if user == "" {
|
if user == "" {
|
||||||
@@ -191,17 +184,16 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
|
|||||||
metadata.User = user
|
metadata.User = user
|
||||||
}
|
}
|
||||||
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
||||||
return h.router.RouteConnection(ctx, conn, metadata)
|
h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (h *Inbound) newPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
metadata.Inbound = h.Tag()
|
metadata.Inbound = h.Tag()
|
||||||
metadata.InboundType = h.Type()
|
metadata.InboundType = h.Type()
|
||||||
metadata.InboundDetour = h.listener.ListenOptions().Detour
|
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
|
||||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return os.ErrInvalid
|
N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
user := h.users[userIndex].Name
|
user := h.users[userIndex].Name
|
||||||
if user == "" {
|
if user == "" {
|
||||||
@@ -216,7 +208,7 @@ func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, me
|
|||||||
} else {
|
} else {
|
||||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||||
}
|
}
|
||||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ adapter.V2RayServerTransportHandler = (*inboundTransportHandler)(nil)
|
var _ adapter.V2RayServerTransportHandler = (*inboundTransportHandler)(nil)
|
||||||
@@ -230,17 +222,3 @@ func (h *inboundTransportHandler) NewConnectionEx(ctx context.Context, conn net.
|
|||||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||||
(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
|
(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) NewError(ctx context.Context, err error) {
|
|
||||||
NewError(h.logger, ctx, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: remove
|
|
||||||
func NewError(logger logger.ContextLogger, ctx context.Context, err error) {
|
|
||||||
common.Close(err)
|
|
||||||
if E.IsClosedOrCanceled(err) {
|
|
||||||
logger.DebugContext(ctx, "connection closed: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.ErrorContext(ctx, err)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -51,19 +51,23 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
}
|
}
|
||||||
if options.Detour == "" {
|
if options.Detour == "" {
|
||||||
options.IsWireGuardListener = true
|
options.IsWireGuardListener = true
|
||||||
} else if options.GSO {
|
|
||||||
return nil, E.New("gso is conflict with detour")
|
|
||||||
}
|
}
|
||||||
outboundDialer, err := dialer.New(ctx, options.DialerOptions)
|
outboundDialer, err := dialer.New(ctx, options.DialerOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
var udpTimeout time.Duration
|
||||||
|
if options.UDPTimeout != 0 {
|
||||||
|
udpTimeout = time.Duration(options.UDPTimeout)
|
||||||
|
} else {
|
||||||
|
udpTimeout = C.UDPTimeout
|
||||||
|
}
|
||||||
wgEndpoint, err := wireguard.NewEndpoint(wireguard.EndpointOptions{
|
wgEndpoint, err := wireguard.NewEndpoint(wireguard.EndpointOptions{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
System: options.System,
|
System: options.System,
|
||||||
Handler: ep,
|
Handler: ep,
|
||||||
UDPTimeout: time.Duration(options.UDPTimeout),
|
UDPTimeout: udpTimeout,
|
||||||
Dialer: outboundDialer,
|
Dialer: outboundDialer,
|
||||||
CreateDialer: func(interfaceName string) N.Dialer {
|
CreateDialer: func(interfaceName string) N.Dialer {
|
||||||
return common.Must1(dialer.NewDefault(service.FromContext[adapter.NetworkManager](ctx), option.DialerOptions{
|
return common.Must1(dialer.NewDefault(service.FromContext[adapter.NetworkManager](ctx), option.DialerOptions{
|
||||||
@@ -72,7 +76,6 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
},
|
},
|
||||||
Name: options.Name,
|
Name: options.Name,
|
||||||
MTU: options.MTU,
|
MTU: options.MTU,
|
||||||
GSO: options.GSO,
|
|
||||||
Address: options.Address,
|
Address: options.Address,
|
||||||
PrivateKey: options.PrivateKey,
|
PrivateKey: options.PrivateKey,
|
||||||
ListenPort: options.ListenPort,
|
ListenPort: options.ListenPort,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user