mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 20:28:32 +10:00
Compare commits
38 Commits
v1.11.0-be
...
v1.11.0-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ddcf40f5d | ||
|
|
3c89cddb6e | ||
|
|
0b75e31ecc | ||
|
|
3a02b982a0 | ||
|
|
6f8c59b838 | ||
|
|
d91350975b | ||
|
|
e3d9f56d7a | ||
|
|
8610018f3b | ||
|
|
7ff4dd541f | ||
|
|
0751506441 | ||
|
|
762418ac16 | ||
|
|
cba8fa185f | ||
|
|
2a1ce5b6ec | ||
|
|
012b0af2ab | ||
|
|
1dd21c9b2b | ||
|
|
f476657ef6 | ||
|
|
da4e199bd5 | ||
|
|
05bda2ef0b | ||
|
|
d68acbc353 | ||
|
|
d34dda9672 | ||
|
|
9aceb133f1 | ||
|
|
e926d15973 | ||
|
|
f1f5a1c60a | ||
|
|
9229769d86 | ||
|
|
6f2af49154 | ||
|
|
ed75e44989 | ||
|
|
ac87be3651 | ||
|
|
303e0f0b0f | ||
|
|
76fdcaa73a | ||
|
|
355e5eebb7 | ||
|
|
05367f3f24 | ||
|
|
d61c622474 | ||
|
|
3ddfc7028b | ||
|
|
153172799d | ||
|
|
aa2ac3e020 | ||
|
|
ae2346b5fc | ||
|
|
2a4bcddbcb | ||
|
|
08d8378a1c |
586
.github/workflows/build.yml
vendored
586
.github/workflows/build.yml
vendored
@@ -1,586 +0,0 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Version name"
|
||||
required: true
|
||||
type: string
|
||||
build:
|
||||
description: "Build type"
|
||||
required: true
|
||||
type: choice
|
||||
default: "All"
|
||||
options:
|
||||
- All
|
||||
- Binary
|
||||
- Android
|
||||
- Apple
|
||||
- app-store
|
||||
- iOS
|
||||
- macOS
|
||||
- tvOS
|
||||
- macOS-standalone
|
||||
- publish-android
|
||||
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 }}
|
||||
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 "version=${{ inputs.version }}" >> "$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"
|
||||
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: github.ref == 'refs/heads/main-next' && github.event_name != 'workflow_dispatch'
|
||||
run: |-
|
||||
cd clients/android
|
||||
git checkout main
|
||||
- name: Checkout dev branch
|
||||
if: github.ref == 'refs/heads/dev-next'
|
||||
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: github.ref == 'refs/heads/main-next' && github.event_name != 'workflow_dispatch'
|
||||
run: |-
|
||||
cd clients/android
|
||||
git checkout main
|
||||
- name: Checkout dev branch
|
||||
if: github.ref == 'refs/heads/dev-next'
|
||||
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:
|
||||
name: Build Apple clients
|
||||
runs-on: macos-15
|
||||
needs:
|
||||
- calculate_version
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: iOS
|
||||
if: ${{ github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store'|| inputs.build == 'iOS' }}
|
||||
platform: 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' }}
|
||||
platform: 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' }}
|
||||
platform: 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' }}
|
||||
platform: macos
|
||||
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 stable
|
||||
if: matrix.if && github.ref == 'refs/heads/main-next'
|
||||
run: |-
|
||||
sudo xcode-select -s /Applications/Xcode_16.1.app
|
||||
- name: Setup Xcode beta
|
||||
if: matrix.if && github.ref == 'refs/heads/dev-next'
|
||||
run: |-
|
||||
sudo xcode-select -s /Applications/Xcode_16.2.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 && github.ref == 'refs/heads/main-next' && github.event_name != 'workflow_dispatch'
|
||||
run: |-
|
||||
cd clients/apple
|
||||
git checkout main
|
||||
- name: Checkout dev branch
|
||||
if: matrix.if && github.ref == 'refs/heads/dev-next'
|
||||
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
|
||||
|
||||
echo "ASC_KEY_PATH=$ASC_KEY_PATH" >> "$GITHUB_ENV"
|
||||
echo "ASC_KEY_ID=$ASC_KEY_ID" >> "$GITHUB_ENV"
|
||||
echo "ASC_KEY_ISSUER_ID=$ASC_KEY_ISSUER_ID" >> "$GITHUB_ENV"
|
||||
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: Build library
|
||||
if: matrix.if
|
||||
run: |-
|
||||
make lib_install
|
||||
export PATH="$PATH:$(go env GOPATH)/bin"
|
||||
go run ./cmd/internal/build_libbox -target apple -platform ${{ matrix.platform }}
|
||||
mv Libbox.xcframework clients/apple
|
||||
- name: Update macOS version
|
||||
if: matrix.if && matrix.name == 'macOS' && github.event_name == 'workflow_dispatch'
|
||||
run: |-
|
||||
MACOS_PROJECT_VERSION=$(go run -v ./cmd/internal/app_store_connect next_macos_project_version)
|
||||
echo "MACOS_PROJECT_VERSION=$MACOS_PROJECT_VERSION"
|
||||
echo "MACOS_PROJECT_VERSION=$MACOS_PROJECT_VERSION" >> "$GITHUB_ENV"
|
||||
- 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 $ASC_KEY_PATH \
|
||||
-authenticationKeyID $ASC_KEY_ID \
|
||||
-authenticationKeyIssuerID $ASC_KEY_ISSUER_ID
|
||||
- name: Upload to App Store Connect
|
||||
if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch'
|
||||
run: |-
|
||||
go run -v ./cmd/internal/app_store_connect cancel_app_store ${{ matrix.platform }}
|
||||
cd clients/apple
|
||||
xcodebuild -exportArchive \
|
||||
-archivePath "${{ matrix.archive }}" \
|
||||
-exportOptionsPlist ${{ matrix.upload }} \
|
||||
-allowProvisioningUpdates \
|
||||
-authenticationKeyPath $ASC_KEY_PATH \
|
||||
-authenticationKeyID $ASC_KEY_ID \
|
||||
-authenticationKeyIssuerID $ASC_KEY_ISSUER_ID
|
||||
- name: Publish to TestFlight
|
||||
if: matrix.if && matrix.name != 'macOS-standalone' && github.event_name == 'workflow_dispatch'
|
||||
run: |-
|
||||
go run -v ./cmd/internal/app_store_connect publish_testflight ${{ matrix.platform }}
|
||||
- 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 == 'All' || inputs.build == 'Binary' || inputs.build == 'Android' || inputs.build == 'Apple' || inputs.build == 'macOS-standalone')
|
||||
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
Normal file
219
.github/workflows/debug.yml
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
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_gvisor,with_dhcp,with_wireguard,with_clash_api,with_quic,with_utls,with_ech
|
||||
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,6 +22,7 @@ jobs:
|
||||
mkdir -p $HOME/.gnupg
|
||||
cat > $HOME/.gnupg/sagernet.key <<EOF
|
||||
${{ secrets.GPG_KEY }}
|
||||
echo "HOME=$HOME" >> "$GITHUB_ENV"
|
||||
EOF
|
||||
echo "HOME=$HOME" >> "$GITHUB_ENV"
|
||||
- name: Publish release
|
||||
|
||||
@@ -200,6 +200,4 @@ release:
|
||||
ids:
|
||||
- archive
|
||||
- package
|
||||
skip_upload: true
|
||||
partial:
|
||||
by: target
|
||||
skip_upload: true
|
||||
26
Makefile
26
Makefile
@@ -71,7 +71,7 @@ release:
|
||||
dist/*_amd64.pkg.tar.zst \
|
||||
dist/*_arm64.pkg.tar.zst \
|
||||
dist/release
|
||||
ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist/release
|
||||
ghr --replace --draft --prerelease -p 3 "v${VERSION}" dist/release
|
||||
rm -r dist/release
|
||||
|
||||
release_repo:
|
||||
@@ -90,7 +90,7 @@ upload_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/other/release/*-universal.apk dist/release_android
|
||||
ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist/release_android
|
||||
ghr --replace --draft --prerelease -p 3 "v${VERSION}" dist/release_android
|
||||
rm -rf dist/release_android
|
||||
|
||||
release_android: lib_android update_android_version build_android upload_android
|
||||
@@ -99,11 +99,9 @@ publish_android:
|
||||
cd ../sing-box-for-android && ./gradlew :app:publishPlayReleaseBundle && ./gradlew --stop
|
||||
|
||||
# TODO: find why and remove `-destination 'generic/platform=iOS'`
|
||||
# TODO: remove xcode clean when fix control widget fixed
|
||||
build_ios:
|
||||
cd ../sing-box-for-apple && \
|
||||
rm -rf build/SFI.xcarchive && \
|
||||
xcodebuild clean -scheme SFI && \
|
||||
xcodebuild archive -scheme SFI -configuration Release -destination 'generic/platform=iOS' -archivePath build/SFI.xcarchive -allowProvisioningUpdates
|
||||
|
||||
upload_ios_app_store:
|
||||
@@ -182,22 +180,10 @@ release_tvos: build_tvos upload_tvos_app_store
|
||||
update_apple_version:
|
||||
go run ./cmd/internal/update_apple_version
|
||||
|
||||
update_macos_version:
|
||||
MACOS_PROJECT_VERSION=$(shell go run -v ./cmd/internal/app_store_connect next_macos_project_version) go run ./cmd/internal/update_apple_version
|
||||
|
||||
release_apple: lib_ios update_apple_version release_ios release_macos release_tvos release_macos_standalone
|
||||
|
||||
release_apple_beta: update_apple_version release_ios release_macos release_tvos
|
||||
|
||||
publish_testflight:
|
||||
go run -v ./cmd/internal/app_store_connect publish_testflight
|
||||
|
||||
prepare_app_store:
|
||||
go run -v ./cmd/internal/app_store_connect prepare_app_store
|
||||
|
||||
publish_app_store:
|
||||
go run -v ./cmd/internal/app_store_connect publish_app_store
|
||||
|
||||
test:
|
||||
@go test -v ./... && \
|
||||
cd test && \
|
||||
@@ -213,14 +199,8 @@ test_stdio:
|
||||
lib_android:
|
||||
go run ./cmd/internal/build_libbox -target android
|
||||
|
||||
lib_android_debug:
|
||||
go run ./cmd/internal/build_libbox -target android -debug
|
||||
|
||||
lib_apple:
|
||||
go run ./cmd/internal/build_libbox -target apple
|
||||
|
||||
lib_ios:
|
||||
go run ./cmd/internal/build_libbox -target apple -platform ios -debug
|
||||
go run ./cmd/internal/build_libbox -target ios
|
||||
|
||||
lib:
|
||||
go run ./cmd/internal/build_libbox -target android
|
||||
|
||||
@@ -8,7 +8,8 @@ import (
|
||||
)
|
||||
|
||||
type ConnectionManager interface {
|
||||
Lifecycle
|
||||
Start() error
|
||||
Close() error
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -70,12 +70,10 @@ type InboundContext struct {
|
||||
InboundOptions option.InboundOptions
|
||||
UDPDisableDomainUnmapping bool
|
||||
UDPConnect bool
|
||||
UDPTimeout time.Duration
|
||||
|
||||
NetworkStrategy *C.NetworkStrategy
|
||||
NetworkType []C.InterfaceType
|
||||
FallbackNetworkType []C.InterfaceType
|
||||
FallbackDelay time.Duration
|
||||
NetworkStrategy C.NetworkStrategy
|
||||
NetworkType []C.InterfaceType
|
||||
FallbackNetworkType []C.InterfaceType
|
||||
FallbackDelay time.Duration
|
||||
|
||||
DNSServer string
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ type NetworkManager interface {
|
||||
}
|
||||
|
||||
type NetworkOptions struct {
|
||||
NetworkStrategy *C.NetworkStrategy
|
||||
NetworkStrategy C.NetworkStrategy
|
||||
NetworkType []C.InterfaceType
|
||||
FallbackNetworkType []C.InterfaceType
|
||||
FallbackDelay time.Duration
|
||||
|
||||
157
adapter/outbound/default.go
Normal file
157
adapter/outbound/default.go
Normal file
@@ -0,0 +1,157 @@
|
||||
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)
|
||||
}
|
||||
26
box.go
26
box.go
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/sagernet/sing-box/adapter/outbound"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental"
|
||||
"github.com/sagernet/sing-box/experimental/cachefile"
|
||||
@@ -150,14 +149,6 @@ func New(options Options) (*Box, error) {
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize router")
|
||||
}
|
||||
|
||||
ntpOptions := common.PtrValueOrDefault(options.NTP)
|
||||
var timeService *tls.TimeServiceWrapper
|
||||
if ntpOptions.Enabled {
|
||||
timeService = new(tls.TimeServiceWrapper)
|
||||
service.MustRegister[ntp.TimeService](ctx, timeService)
|
||||
}
|
||||
|
||||
for i, endpointOptions := range options.Endpoints {
|
||||
var tag string
|
||||
if endpointOptions.Tag != "" {
|
||||
@@ -263,12 +254,13 @@ func New(options Options) (*Box, error) {
|
||||
service.MustRegister[adapter.V2RayServer](ctx, v2rayServer)
|
||||
}
|
||||
}
|
||||
ntpOptions := common.PtrValueOrDefault(options.NTP)
|
||||
if ntpOptions.Enabled {
|
||||
ntpDialer, err := dialer.New(ctx, ntpOptions.DialerOptions)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create NTP service")
|
||||
}
|
||||
ntpService := ntp.NewService(ntp.Options{
|
||||
timeService := ntp.NewService(ntp.Options{
|
||||
Context: ctx,
|
||||
Dialer: ntpDialer,
|
||||
Logger: logFactory.NewLogger("ntp"),
|
||||
@@ -276,8 +268,8 @@ func New(options Options) (*Box, error) {
|
||||
Interval: time.Duration(ntpOptions.Interval),
|
||||
WriteToSystem: ntpOptions.WriteToSystem,
|
||||
})
|
||||
timeService.TimeService = ntpService
|
||||
services = append(services, adapter.NewLifecycleService(ntpService, "ntp service"))
|
||||
service.MustRegister[ntp.TimeService](ctx, timeService)
|
||||
services = append(services, adapter.NewLifecycleService(timeService, "ntp service"))
|
||||
}
|
||||
return &Box{
|
||||
network: networkManager,
|
||||
@@ -344,11 +336,11 @@ func (s *Box) preStart() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(adapter.StartStateInitialize, s.network, s.connection, s.router, s.outbound, s.inbound, s.endpoint)
|
||||
err = adapter.Start(adapter.StartStateInitialize, s.network, s.router, s.outbound, s.inbound, s.endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(adapter.StartStateStart, s.outbound, s.network, s.connection, s.router)
|
||||
err = adapter.Start(adapter.StartStateStart, s.outbound, s.network, s.router)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -372,7 +364,7 @@ func (s *Box) start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(adapter.StartStatePostStart, s.outbound, s.network, s.connection, s.router, s.inbound, s.endpoint)
|
||||
err = adapter.Start(adapter.StartStatePostStart, s.outbound, s.network, s.router, s.inbound, s.endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -380,7 +372,7 @@ func (s *Box) start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(adapter.StartStateStarted, s.network, s.connection, s.router, s.outbound, s.inbound, s.endpoint)
|
||||
err = adapter.Start(adapter.StartStateStarted, s.network, s.router, s.outbound, s.inbound, s.endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -399,7 +391,7 @@ func (s *Box) Close() error {
|
||||
close(s.done)
|
||||
}
|
||||
err := common.Close(
|
||||
s.inbound, s.outbound, s.router, s.connection, s.network,
|
||||
s.inbound, s.outbound, s.router, s.network,
|
||||
)
|
||||
for _, lifecycleService := range s.services {
|
||||
err = E.Append(err, lifecycleService.Close(), func(err error) error {
|
||||
|
||||
Submodule clients/android updated: db039bde63...ea460ea5d1
Submodule clients/apple updated: a5d56af571...286f9717cb
@@ -1,30 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/cidertool/asc-go/asc"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
*asc.Client
|
||||
}
|
||||
|
||||
func (c *Client) UpdateBuildForAppStoreVersion(ctx context.Context, id string, buildID *string) (*asc.Response, error) {
|
||||
linkage := newRelationshipDeclaration(buildID, "builds")
|
||||
url := fmt.Sprintf("appStoreVersions/%s/relationships/build", id)
|
||||
return c.patch(ctx, url, newRequestBody(linkage), nil)
|
||||
}
|
||||
|
||||
func newRelationshipDeclaration(id *string, relationshipType string) *asc.RelationshipData {
|
||||
if id == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &asc.RelationshipData{
|
||||
ID: *id,
|
||||
Type: relationshipType,
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/cidertool/asc-go/asc"
|
||||
"github.com/google/go-querystring/query"
|
||||
)
|
||||
|
||||
func (c *Client) newRequest(ctx context.Context, method string, path string, body *requestBody, options ...requestOption) (*http.Request, error) {
|
||||
return clientNewRequest(c.Client, ctx, method, path, body, options...)
|
||||
}
|
||||
|
||||
//go:linkname clientNewRequest github.com/cidertool/asc-go/asc.(*Client).newRequest
|
||||
func clientNewRequest(c *asc.Client, ctx context.Context, method string, path string, body *requestBody, options ...requestOption) (*http.Request, error)
|
||||
|
||||
func (c *Client) do(ctx context.Context, req *http.Request, v interface{}) (*asc.Response, error) {
|
||||
return clientDo(c.Client, ctx, req, v)
|
||||
}
|
||||
|
||||
//go:linkname clientDo github.com/cidertool/asc-go/asc.(*Client).do
|
||||
func clientDo(c *asc.Client, ctx context.Context, req *http.Request, v interface{}) (*asc.Response, error)
|
||||
|
||||
// get sends a GET request to the API as configured.
|
||||
func (c *Client) get(ctx context.Context, url string, query interface{}, v interface{}, options ...requestOption) (*asc.Response, error) {
|
||||
var err error
|
||||
if query != nil {
|
||||
url, err = appendingQueryOptions(url, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
req, err := c.newRequest(ctx, "GET", url, nil, options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.do(ctx, req, v)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// post sends a POST request to the API as configured.
|
||||
func (c *Client) post(ctx context.Context, url string, body *requestBody, v interface{}) (*asc.Response, error) {
|
||||
req, err := c.newRequest(ctx, "POST", url, body, withContentType("application/json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.do(ctx, req, v)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// patch sends a PATCH request to the API as configured.
|
||||
func (c *Client) patch(ctx context.Context, url string, body *requestBody, v interface{}) (*asc.Response, error) {
|
||||
req, err := c.newRequest(ctx, "PATCH", url, body, withContentType("application/json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.do(ctx, req, v)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// delete sends a DELETE request to the API as configured.
|
||||
func (c *Client) delete(ctx context.Context, url string, body *requestBody) (*asc.Response, error) {
|
||||
req, err := c.newRequest(ctx, "DELETE", url, body, withContentType("application/json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.do(ctx, req, nil)
|
||||
}
|
||||
|
||||
// request is a common structure for a request body sent to the API.
|
||||
type requestBody struct {
|
||||
Data interface{} `json:"data"`
|
||||
Included interface{} `json:"included,omitempty"`
|
||||
}
|
||||
|
||||
func newRequestBody(data interface{}) *requestBody {
|
||||
return newRequestBodyWithIncluded(data, nil)
|
||||
}
|
||||
|
||||
func newRequestBodyWithIncluded(data interface{}, included interface{}) *requestBody {
|
||||
return &requestBody{Data: data, Included: included}
|
||||
}
|
||||
|
||||
type requestOption func(*http.Request)
|
||||
|
||||
func withAccept(typ string) requestOption {
|
||||
return func(req *http.Request) {
|
||||
req.Header.Set("Accept", typ)
|
||||
}
|
||||
}
|
||||
|
||||
func withContentType(typ string) requestOption {
|
||||
return func(req *http.Request) {
|
||||
req.Header.Set("Content-Type", typ)
|
||||
}
|
||||
}
|
||||
|
||||
// AddOptions adds the parameters in opt as URL query parameters to s. opt
|
||||
// must be a struct whose fields may contain "url" tags.
|
||||
func appendingQueryOptions(s string, opt interface{}) (string, error) {
|
||||
v := reflect.ValueOf(opt)
|
||||
if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return s, err
|
||||
}
|
||||
|
||||
qs, err := query.Values(opt)
|
||||
if err != nil {
|
||||
return s, err
|
||||
}
|
||||
|
||||
u.RawQuery = qs.Encode()
|
||||
|
||||
return u.String(), nil
|
||||
}
|
||||
@@ -1,430 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
|
||||
"github.com/cidertool/asc-go/asc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
switch os.Args[1] {
|
||||
case "next_macos_project_version":
|
||||
err := fetchMacOSVersion(ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
case "publish_testflight":
|
||||
err := publishTestflight(ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
case "cancel_app_store":
|
||||
err := cancelAppStore(ctx, os.Args[2])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
case "prepare_app_store":
|
||||
err := prepareAppStore(ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
case "publish_app_store":
|
||||
err := publishAppStore(ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
default:
|
||||
log.Fatal("unknown action: ", os.Args[1])
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
appID = "6673731168"
|
||||
groupID = "5c5f3b78-b7a0-40c0-bcad-e6ef87bbefda"
|
||||
)
|
||||
|
||||
func createClient(expireDuration time.Duration) *Client {
|
||||
privateKey, err := os.ReadFile(os.Getenv("ASC_KEY_PATH"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tokenConfig, err := asc.NewTokenConfig(os.Getenv("ASC_KEY_ID"), os.Getenv("ASC_KEY_ISSUER_ID"), expireDuration, privateKey)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return &Client{asc.NewClient(tokenConfig.Client())}
|
||||
}
|
||||
|
||||
func fetchMacOSVersion(ctx context.Context) error {
|
||||
client := createClient(time.Minute)
|
||||
versions, _, err := client.Apps.ListAppStoreVersionsForApp(ctx, appID, &asc.ListAppStoreVersionsQuery{
|
||||
FilterPlatform: []string{"MAC_OS"},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var versionID string
|
||||
findVersion:
|
||||
for _, version := range versions.Data {
|
||||
switch *version.Attributes.AppStoreState {
|
||||
case asc.AppStoreVersionStateReadyForSale,
|
||||
asc.AppStoreVersionStatePendingDeveloperRelease:
|
||||
versionID = version.ID
|
||||
break findVersion
|
||||
}
|
||||
}
|
||||
if versionID == "" {
|
||||
return E.New("no version found")
|
||||
}
|
||||
latestBuild, _, err := client.Builds.GetBuildForAppStoreVersion(ctx, versionID, &asc.GetBuildForAppStoreVersionQuery{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
versionInt, err := strconv.Atoi(*latestBuild.Data.Attributes.Version)
|
||||
if err != nil {
|
||||
return E.Cause(err, "parse version code")
|
||||
}
|
||||
os.Stdout.WriteString(F.ToString(versionInt+1, "\n"))
|
||||
return nil
|
||||
}
|
||||
|
||||
func publishTestflight(ctx context.Context) error {
|
||||
tagVersion, err := build_shared.ReadTagVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tag := tagVersion.VersionString()
|
||||
client := createClient(10 * time.Minute)
|
||||
|
||||
log.Info(tag, " list build IDs")
|
||||
buildIDsResponse, _, err := client.TestFlight.ListBuildIDsForBetaGroup(ctx, groupID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buildIDs := common.Map(buildIDsResponse.Data, func(it asc.RelationshipData) string {
|
||||
return it.ID
|
||||
})
|
||||
var platforms []asc.Platform
|
||||
if len(os.Args) == 3 {
|
||||
switch os.Args[2] {
|
||||
case "ios":
|
||||
platforms = []asc.Platform{asc.PlatformIOS}
|
||||
case "macos":
|
||||
platforms = []asc.Platform{asc.PlatformMACOS}
|
||||
case "tvos":
|
||||
platforms = []asc.Platform{asc.PlatformTVOS}
|
||||
default:
|
||||
return E.New("unknown platform: ", os.Args[2])
|
||||
}
|
||||
} else {
|
||||
platforms = []asc.Platform{
|
||||
asc.PlatformIOS,
|
||||
asc.PlatformMACOS,
|
||||
asc.PlatformTVOS,
|
||||
}
|
||||
}
|
||||
for _, platform := range platforms {
|
||||
log.Info(string(platform), " list builds")
|
||||
for {
|
||||
builds, _, err := client.Builds.ListBuilds(ctx, &asc.ListBuildsQuery{
|
||||
FilterApp: []string{appID},
|
||||
FilterPreReleaseVersionPlatform: []string{string(platform)},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
build := builds.Data[0]
|
||||
if common.Contains(buildIDs, build.ID) || time.Since(build.Attributes.UploadedDate.Time) > 5*time.Minute {
|
||||
log.Info(string(platform), " ", tag, " waiting for process")
|
||||
time.Sleep(15 * time.Second)
|
||||
continue
|
||||
}
|
||||
if *build.Attributes.ProcessingState != "VALID" {
|
||||
log.Info(string(platform), " ", tag, " waiting for process: ", *build.Attributes.ProcessingState)
|
||||
time.Sleep(15 * time.Second)
|
||||
continue
|
||||
}
|
||||
log.Info(string(platform), " ", tag, " list localizations")
|
||||
localizations, _, err := client.TestFlight.ListBetaBuildLocalizationsForBuild(ctx, build.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
localization := common.Find(localizations.Data, func(it asc.BetaBuildLocalization) bool {
|
||||
return *it.Attributes.Locale == "en-US"
|
||||
})
|
||||
if localization.ID == "" {
|
||||
log.Fatal(string(platform), " ", tag, " no en-US localization found")
|
||||
}
|
||||
if localization.Attributes == nil || localization.Attributes.WhatsNew == nil || *localization.Attributes.WhatsNew == "" {
|
||||
log.Info(string(platform), " ", tag, " update localization")
|
||||
_, _, err = client.TestFlight.UpdateBetaBuildLocalization(ctx, localization.ID, common.Ptr(
|
||||
F.ToString("sing-box ", tag),
|
||||
))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Info(string(platform), " ", tag, " publish")
|
||||
response, err := client.TestFlight.AddBuildsToBetaGroup(ctx, groupID, []string{build.ID})
|
||||
if response != nil && response.StatusCode == http.StatusUnprocessableEntity {
|
||||
log.Info("waiting for process")
|
||||
time.Sleep(15 * time.Second)
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info(string(platform), " ", tag, " list submissions")
|
||||
betaSubmissions, _, err := client.TestFlight.ListBetaAppReviewSubmissions(ctx, &asc.ListBetaAppReviewSubmissionsQuery{
|
||||
FilterBuild: []string{build.ID},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(betaSubmissions.Data) == 0 {
|
||||
log.Info(string(platform), " ", tag, " create submission")
|
||||
_, _, err = client.TestFlight.CreateBetaAppReviewSubmission(ctx, build.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cancelAppStore(ctx context.Context, platform string) error {
|
||||
switch platform {
|
||||
case "ios":
|
||||
platform = string(asc.PlatformIOS)
|
||||
case "macos":
|
||||
platform = string(asc.PlatformMACOS)
|
||||
case "tvos":
|
||||
platform = string(asc.PlatformTVOS)
|
||||
}
|
||||
tag, err := build_shared.ReadTag()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := createClient(time.Minute)
|
||||
log.Info(platform, " list versions")
|
||||
versions, _, err := client.Apps.ListAppStoreVersionsForApp(ctx, appID, &asc.ListAppStoreVersionsQuery{
|
||||
FilterPlatform: []string{string(platform)},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
version := common.Find(versions.Data, func(it asc.AppStoreVersion) bool {
|
||||
return *it.Attributes.VersionString == tag
|
||||
})
|
||||
if version.ID == "" {
|
||||
return nil
|
||||
}
|
||||
log.Info(string(platform), " ", tag, " get submission")
|
||||
submission, response, err := client.Submission.GetAppStoreVersionSubmissionForAppStoreVersion(ctx, version.ID, nil)
|
||||
if response != nil && response.StatusCode == http.StatusNotFound {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info(platform, " ", tag, " delete submission")
|
||||
_, err = client.Submission.DeleteSubmission(ctx, submission.Data.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func prepareAppStore(ctx context.Context) error {
|
||||
tag, err := build_shared.ReadTag()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := createClient(time.Minute)
|
||||
for _, platform := range []asc.Platform{
|
||||
asc.PlatformIOS,
|
||||
asc.PlatformMACOS,
|
||||
asc.PlatformTVOS,
|
||||
} {
|
||||
log.Info(string(platform), " list versions")
|
||||
versions, _, err := client.Apps.ListAppStoreVersionsForApp(ctx, appID, &asc.ListAppStoreVersionsQuery{
|
||||
FilterPlatform: []string{string(platform)},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
version := common.Find(versions.Data, func(it asc.AppStoreVersion) bool {
|
||||
return *it.Attributes.VersionString == tag
|
||||
})
|
||||
log.Info(string(platform), " ", tag, " list builds")
|
||||
builds, _, err := client.Builds.ListBuilds(ctx, &asc.ListBuildsQuery{
|
||||
FilterApp: []string{appID},
|
||||
FilterPreReleaseVersionPlatform: []string{string(platform)},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(builds.Data) == 0 {
|
||||
log.Fatal(platform, " ", tag, " no build found")
|
||||
}
|
||||
buildID := common.Ptr(builds.Data[0].ID)
|
||||
if version.ID == "" {
|
||||
log.Info(string(platform), " ", tag, " create version")
|
||||
newVersion, _, err := client.Apps.CreateAppStoreVersion(ctx, asc.AppStoreVersionCreateRequestAttributes{
|
||||
Platform: platform,
|
||||
VersionString: tag,
|
||||
}, appID, buildID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
version = newVersion.Data
|
||||
|
||||
} else {
|
||||
log.Info(string(platform), " ", tag, " check build")
|
||||
currentBuild, response, err := client.Apps.GetBuildIDForAppStoreVersion(ctx, version.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response.StatusCode != http.StatusOK || currentBuild.Data.ID != *buildID {
|
||||
switch *version.Attributes.AppStoreState {
|
||||
case asc.AppStoreVersionStatePrepareForSubmission,
|
||||
asc.AppStoreVersionStateRejected,
|
||||
asc.AppStoreVersionStateDeveloperRejected:
|
||||
case asc.AppStoreVersionStateWaitingForReview,
|
||||
asc.AppStoreVersionStateInReview,
|
||||
asc.AppStoreVersionStatePendingDeveloperRelease:
|
||||
submission, _, err := client.Submission.GetAppStoreVersionSubmissionForAppStoreVersion(ctx, version.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if submission != nil {
|
||||
log.Info(string(platform), " ", tag, " delete submission")
|
||||
_, err = client.Submission.DeleteSubmission(ctx, submission.Data.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
default:
|
||||
log.Fatal(string(platform), " ", tag, " unknown state ", string(*version.Attributes.AppStoreState))
|
||||
}
|
||||
log.Info(string(platform), " ", tag, " update build")
|
||||
response, err = client.UpdateBuildForAppStoreVersion(ctx, version.ID, buildID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response.StatusCode != http.StatusNoContent {
|
||||
response.Write(os.Stderr)
|
||||
log.Fatal(string(platform), " ", tag, " unexpected response: ", response.Status)
|
||||
}
|
||||
} else {
|
||||
switch *version.Attributes.AppStoreState {
|
||||
case asc.AppStoreVersionStatePrepareForSubmission,
|
||||
asc.AppStoreVersionStateRejected,
|
||||
asc.AppStoreVersionStateDeveloperRejected:
|
||||
case asc.AppStoreVersionStateWaitingForReview,
|
||||
asc.AppStoreVersionStateInReview,
|
||||
asc.AppStoreVersionStatePendingDeveloperRelease:
|
||||
continue
|
||||
default:
|
||||
log.Fatal(string(platform), " ", tag, " unknown state ", string(*version.Attributes.AppStoreState))
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Info(string(platform), " ", tag, " list localization")
|
||||
localizations, _, err := client.Apps.ListLocalizationsForAppStoreVersion(ctx, version.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
localization := common.Find(localizations.Data, func(it asc.AppStoreVersionLocalization) bool {
|
||||
return *it.Attributes.Locale == "en-US"
|
||||
})
|
||||
if localization.ID == "" {
|
||||
log.Info(string(platform), " ", tag, " no en-US localization found")
|
||||
}
|
||||
if localization.Attributes.WhatsNew == nil && *localization.Attributes.WhatsNew == "" {
|
||||
log.Info(string(platform), " ", tag, " update localization")
|
||||
_, _, err = client.Apps.UpdateAppStoreVersionLocalization(ctx, localization.ID, &asc.AppStoreVersionLocalizationUpdateRequestAttributes{
|
||||
PromotionalText: common.Ptr("Yet another distribution for sing-box, the universal proxy platform."),
|
||||
WhatsNew: common.Ptr(F.ToString("sing-box ", tag, ": Fixes and improvements.")),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Info(string(platform), " ", tag, " create submission")
|
||||
fixSubmit:
|
||||
for {
|
||||
_, response, err := client.Submission.CreateSubmission(ctx, version.ID)
|
||||
if err != nil {
|
||||
switch response.StatusCode {
|
||||
case http.StatusInternalServerError:
|
||||
continue
|
||||
default:
|
||||
response.Write(os.Stderr)
|
||||
log.Info(string(platform), " ", tag, " unexpected response: ", response.Status)
|
||||
}
|
||||
}
|
||||
switch response.StatusCode {
|
||||
case http.StatusCreated:
|
||||
break fixSubmit
|
||||
default:
|
||||
response.Write(os.Stderr)
|
||||
log.Info(string(platform), " ", tag, " unexpected response: ", response.Status)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func publishAppStore(ctx context.Context) error {
|
||||
tag, err := build_shared.ReadTag()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := createClient(time.Minute)
|
||||
for _, platform := range []asc.Platform{
|
||||
asc.PlatformIOS,
|
||||
asc.PlatformMACOS,
|
||||
asc.PlatformTVOS,
|
||||
} {
|
||||
log.Info(string(platform), " list versions")
|
||||
versions, _, err := client.Apps.ListAppStoreVersionsForApp(ctx, appID, &asc.ListAppStoreVersionsQuery{
|
||||
FilterPlatform: []string{string(platform)},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
version := common.Find(versions.Data, func(it asc.AppStoreVersion) bool {
|
||||
return *it.Attributes.VersionString == tag
|
||||
})
|
||||
switch *version.Attributes.AppStoreState {
|
||||
case asc.AppStoreVersionStatePrepareForSubmission, asc.AppStoreVersionStateDeveloperRejected:
|
||||
log.Fatal(string(platform), " ", tag, " not submitted")
|
||||
case asc.AppStoreVersionStateWaitingForReview,
|
||||
asc.AppStoreVersionStateInReview:
|
||||
log.Warn(string(platform), " ", tag, " waiting for review")
|
||||
continue
|
||||
case asc.AppStoreVersionStatePendingDeveloperRelease:
|
||||
default:
|
||||
log.Fatal(string(platform), " ", tag, " unknown state ", string(*version.Attributes.AppStoreState))
|
||||
}
|
||||
_, _, err = client.Publishing.CreatePhasedRelease(ctx, common.Ptr(asc.PhasedReleaseStateComplete), version.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -10,21 +10,17 @@ import (
|
||||
_ "github.com/sagernet/gomobile"
|
||||
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/common/shell"
|
||||
)
|
||||
|
||||
var (
|
||||
debugEnabled bool
|
||||
target string
|
||||
platform string
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&debugEnabled, "debug", false, "enable debug")
|
||||
flag.StringVar(&target, "target", "android", "target platform")
|
||||
flag.StringVar(&platform, "platform", "", "specify platform")
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -35,8 +31,8 @@ func main() {
|
||||
switch target {
|
||||
case "android":
|
||||
buildAndroid()
|
||||
case "apple":
|
||||
buildApple()
|
||||
case "ios":
|
||||
buildiOS()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,35 +62,9 @@ func init() {
|
||||
func buildAndroid() {
|
||||
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 platform != "" {
|
||||
bindTarget = platform
|
||||
} else if debugEnabled {
|
||||
bindTarget = "android/arm64"
|
||||
} else {
|
||||
bindTarget = "android"
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"bind",
|
||||
"-v",
|
||||
"-target", bindTarget,
|
||||
"-androidapi", "21",
|
||||
"-javapkg=io.nekohasekai",
|
||||
"-libname=box",
|
||||
@@ -116,7 +86,7 @@ func buildAndroid() {
|
||||
command := exec.Command(build_shared.GoBinPath+"/gomobile", args...)
|
||||
command.Stdout = os.Stdout
|
||||
command.Stderr = os.Stderr
|
||||
err = command.Run()
|
||||
err := command.Run()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -133,20 +103,11 @@ func buildAndroid() {
|
||||
}
|
||||
}
|
||||
|
||||
func buildApple() {
|
||||
var bindTarget string
|
||||
if platform != "" {
|
||||
bindTarget = platform
|
||||
} else if debugEnabled {
|
||||
bindTarget = "ios"
|
||||
} else {
|
||||
bindTarget = "ios,tvos,macos"
|
||||
}
|
||||
|
||||
func buildiOS() {
|
||||
args := []string{
|
||||
"bind",
|
||||
"-v",
|
||||
"-target", bindTarget,
|
||||
"-target", "ios,iossimulator,tvos,tvossimulator,macos",
|
||||
"-libname=box",
|
||||
}
|
||||
if !debugEnabled {
|
||||
|
||||
@@ -11,7 +11,9 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/common/shell"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -40,6 +42,14 @@ func FindSDK() {
|
||||
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_SDK_HOME", androidSDKPath)
|
||||
os.Setenv("ANDROID_NDK_HOME", androidNDKPath)
|
||||
@@ -48,16 +58,12 @@ func FindSDK() {
|
||||
}
|
||||
|
||||
func findNDK() bool {
|
||||
const fixedVersion = "28.0.12674087"
|
||||
const fixedVersion = "26.2.11394342"
|
||||
const versionFile = "source.properties"
|
||||
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.IsFile(filepath.Join(fixedPath, versionFile)) {
|
||||
androidNDKPath = fixedPath
|
||||
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"))
|
||||
if err != nil {
|
||||
return false
|
||||
|
||||
@@ -20,11 +20,6 @@ func ReadTag() (string, error) {
|
||||
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) {
|
||||
currentTag := common.Must1(shell.Exec("git", "describe", "--tags").ReadOutput())
|
||||
currentTagRev := common.Must1(shell.Exec("git", "describe", "--tags", "--abbrev=0").ReadOutput())
|
||||
|
||||
@@ -1,62 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
)
|
||||
|
||||
var nightly bool
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&nightly, "nightly", false, "Print nightly tag")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if nightly {
|
||||
version, err := build_shared.ReadTagVersionRev()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var versionStr string
|
||||
if version.PreReleaseIdentifier != "" {
|
||||
versionStr = version.VersionString() + "-nightly"
|
||||
} else {
|
||||
version.Patch++
|
||||
versionStr = version.VersionString() + "-nightly"
|
||||
}
|
||||
err = setGitHubEnv("version", versionStr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
currentTag, err := build_shared.ReadTag()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
_, err = os.Stdout.WriteString("unknown\n")
|
||||
} else {
|
||||
tag, err := build_shared.ReadTag()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
os.Stdout.WriteString("unknown\n")
|
||||
} else {
|
||||
os.Stdout.WriteString(tag + "\n")
|
||||
}
|
||||
_, err = os.Stdout.WriteString(currentTag + "\n")
|
||||
}
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func setGitHubEnv(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,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -13,22 +12,9 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
)
|
||||
|
||||
var flagRunInCI bool
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&flagRunInCI, "ci", false, "Run in CI")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
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)
|
||||
newVersion := common.Must1(build_shared.ReadTagVersion())
|
||||
androidPath, err := filepath.Abs("../sing-box-for-android")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -45,10 +31,10 @@ func main() {
|
||||
for _, propPair := range propsList {
|
||||
switch propPair[0] {
|
||||
case "VERSION_NAME":
|
||||
if propPair[1] != newVersion {
|
||||
if propPair[1] != newVersion.String() {
|
||||
versionUpdated = true
|
||||
propPair[1] = newVersion
|
||||
log.Info("updated version to ", newVersion)
|
||||
propPair[1] = newVersion.String()
|
||||
log.Info("updated version to ", newVersion.String())
|
||||
}
|
||||
case "GO_VERSION":
|
||||
if propPair[1] != runtime.Version() {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -14,22 +13,9 @@ import (
|
||||
"howett.net/plist"
|
||||
)
|
||||
|
||||
var flagRunInCI bool
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&flagRunInCI, "ci", false, "Run in CI")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
newVersion := common.Must1(build_shared.ReadTagVersion())
|
||||
var applePath string
|
||||
if flagRunInCI {
|
||||
applePath = "clients/apple"
|
||||
} else {
|
||||
applePath = "../sing-box-for-apple"
|
||||
}
|
||||
applePath, err := filepath.Abs(applePath)
|
||||
applePath, err := filepath.Abs("../sing-box-for-apple")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ func createPreStartedClient() (*box.Box, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
instance, err := box.New(box.Options{Context: globalCtx, Options: options})
|
||||
instance, err := box.New(box.Options{Options: options})
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create service")
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/conntrack"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
@@ -17,7 +16,6 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -35,22 +33,19 @@ type DefaultDialer struct {
|
||||
udpAddr6 string
|
||||
isWireGuardListener bool
|
||||
networkManager adapter.NetworkManager
|
||||
networkStrategy *C.NetworkStrategy
|
||||
networkStrategy C.NetworkStrategy
|
||||
networkType []C.InterfaceType
|
||||
fallbackNetworkType []C.InterfaceType
|
||||
networkFallbackDelay time.Duration
|
||||
networkLastFallback atomic.TypedValue[time.Time]
|
||||
}
|
||||
|
||||
func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDialer, error) {
|
||||
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
||||
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||
|
||||
func NewDefault(networkManager adapter.NetworkManager, options option.DialerOptions) (*DefaultDialer, error) {
|
||||
var (
|
||||
dialer net.Dialer
|
||||
listener net.ListenConfig
|
||||
interfaceFinder control.InterfaceFinder
|
||||
networkStrategy *C.NetworkStrategy
|
||||
networkStrategy C.NetworkStrategy
|
||||
networkType []C.InterfaceType
|
||||
fallbackNetworkType []C.InterfaceType
|
||||
networkFallbackDelay time.Duration
|
||||
@@ -79,35 +74,30 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
||||
listener.Control = control.Append(listener.Control, control.RoutingMark(autoRedirectOutputMark))
|
||||
}
|
||||
}
|
||||
if options.BindInterface != "" || options.Inet4BindAddress != nil || options.Inet6BindAddress != nil || options.TCPFastOpen {
|
||||
if options.NetworkStrategy != nil || len(options.NetworkType) > 0 && options.FallbackNetworkType == nil && options.FallbackDelay == 0 {
|
||||
return nil, E.New("`network_strategy` is conflict with `bind_interface`, `inet4_bind_address`, `inet6_bind_address` and `tcp_fast_open`")
|
||||
if C.NetworkStrategy(options.NetworkStrategy) != C.NetworkStrategyDefault {
|
||||
if options.BindInterface != "" || options.Inet4BindAddress != nil || options.Inet6BindAddress != nil {
|
||||
return nil, E.New("`network_strategy` is conflict with `bind_interface`, `inet4_bind_address` and `inet6_bind_address`")
|
||||
}
|
||||
networkStrategy = C.NetworkStrategy(options.NetworkStrategy)
|
||||
networkType = common.Map(options.NetworkType, option.InterfaceType.Build)
|
||||
fallbackNetworkType = common.Map(options.FallbackNetworkType, option.InterfaceType.Build)
|
||||
networkFallbackDelay = time.Duration(options.NetworkFallbackDelay)
|
||||
if networkManager == nil || !networkManager.AutoDetectInterface() {
|
||||
return nil, E.New("`route.auto_detect_interface` is require by `network_strategy`")
|
||||
}
|
||||
}
|
||||
|
||||
if networkManager != nil {
|
||||
if networkManager != nil && options.BindInterface == "" && options.Inet4BindAddress == nil && options.Inet6BindAddress == nil {
|
||||
defaultOptions := networkManager.DefaultOptions()
|
||||
if defaultOptions.BindInterface != "" {
|
||||
bindFunc := control.BindToInterface(networkManager.InterfaceFinder(), defaultOptions.BindInterface, -1)
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
} else if networkManager.AutoDetectInterface() {
|
||||
if platformInterface != nil {
|
||||
networkStrategy = (*C.NetworkStrategy)(options.NetworkStrategy)
|
||||
if networkStrategy == nil {
|
||||
networkStrategy = common.Ptr(C.NetworkStrategyDefault)
|
||||
}
|
||||
networkType = common.Map(options.NetworkType, option.InterfaceType.Build)
|
||||
fallbackNetworkType = common.Map(options.FallbackNetworkType, option.InterfaceType.Build)
|
||||
if networkStrategy == nil && len(networkType) == 0 && len(fallbackNetworkType) == 0 {
|
||||
networkStrategy = defaultOptions.NetworkStrategy
|
||||
networkType = defaultOptions.NetworkType
|
||||
fallbackNetworkType = defaultOptions.FallbackNetworkType
|
||||
}
|
||||
networkFallbackDelay = time.Duration(options.FallbackDelay)
|
||||
if networkFallbackDelay == 0 && defaultOptions.FallbackDelay != 0 {
|
||||
networkFallbackDelay = defaultOptions.FallbackDelay
|
||||
}
|
||||
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)
|
||||
@@ -117,10 +107,6 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
}
|
||||
}
|
||||
if options.RoutingMark == 0 && defaultOptions.RoutingMark != 0 {
|
||||
dialer.Control = control.Append(dialer.Control, control.RoutingMark(defaultOptions.RoutingMark))
|
||||
listener.Control = control.Append(listener.Control, control.RoutingMark(defaultOptions.RoutingMark))
|
||||
}
|
||||
}
|
||||
if options.ReuseAddr {
|
||||
listener.Control = control.Append(listener.Control, control.ReuseAddr())
|
||||
@@ -180,6 +166,9 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
||||
listener.Control = control.Append(listener.Control, controlFn)
|
||||
}
|
||||
}
|
||||
if networkStrategy != C.NetworkStrategyDefault && options.TCPFastOpen {
|
||||
return nil, E.New("`tcp_fast_open` is conflict with `network_strategy` or `route.default_network_strategy`")
|
||||
}
|
||||
tcpDialer4, err := newTCPDialer(dialer4, options.TCPFastOpen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -209,7 +198,7 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
||||
if !address.IsValid() {
|
||||
return nil, E.New("invalid address")
|
||||
}
|
||||
if d.networkStrategy == nil {
|
||||
if d.networkStrategy == C.NetworkStrategyDefault {
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkUDP:
|
||||
if !address.IsIPv6() {
|
||||
@@ -228,21 +217,12 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network string, address M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||
if strategy == nil {
|
||||
strategy = d.networkStrategy
|
||||
}
|
||||
if strategy == nil {
|
||||
func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network string, address M.Socksaddr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||
if strategy == C.NetworkStrategyDefault {
|
||||
return d.DialContext(ctx, network, address)
|
||||
}
|
||||
if len(interfaceType) == 0 {
|
||||
interfaceType = d.networkType
|
||||
}
|
||||
if len(fallbackInterfaceType) == 0 {
|
||||
fallbackInterfaceType = d.fallbackNetworkType
|
||||
}
|
||||
if fallbackDelay == 0 {
|
||||
fallbackDelay = d.networkFallbackDelay
|
||||
if !d.networkManager.AutoDetectInterface() {
|
||||
return nil, E.New("`route.auto_detect_interface` is require by `network_strategy`")
|
||||
}
|
||||
var dialer net.Dialer
|
||||
if N.NetworkName(network) == N.NetworkTCP {
|
||||
@@ -257,9 +237,9 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
|
||||
err error
|
||||
)
|
||||
if !fastFallback {
|
||||
conn, isPrimary, err = d.dialParallelInterface(ctx, dialer, network, address.String(), *strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||
conn, isPrimary, err = d.dialParallelInterface(ctx, dialer, network, address.String(), strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||
} else {
|
||||
conn, isPrimary, err = d.dialParallelInterfaceFastFallback(ctx, dialer, network, address.String(), *strategy, interfaceType, fallbackInterfaceType, fallbackDelay, d.networkLastFallback.Store)
|
||||
conn, isPrimary, err = d.dialParallelInterfaceFastFallback(ctx, dialer, network, address.String(), strategy, interfaceType, fallbackInterfaceType, fallbackDelay, d.networkLastFallback.Store)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -271,7 +251,7 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
|
||||
}
|
||||
|
||||
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
if d.networkStrategy == nil {
|
||||
if d.networkStrategy == C.NetworkStrategyDefault {
|
||||
if destination.IsIPv6() {
|
||||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6))
|
||||
} else if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
|
||||
@@ -284,31 +264,22 @@ func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksadd
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error) {
|
||||
if strategy == nil {
|
||||
strategy = d.networkStrategy
|
||||
}
|
||||
if strategy == nil {
|
||||
func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error) {
|
||||
if strategy == C.NetworkStrategyDefault {
|
||||
return d.ListenPacket(ctx, destination)
|
||||
}
|
||||
if len(interfaceType) == 0 {
|
||||
interfaceType = d.networkType
|
||||
}
|
||||
if len(fallbackInterfaceType) == 0 {
|
||||
fallbackInterfaceType = d.fallbackNetworkType
|
||||
}
|
||||
if fallbackDelay == 0 {
|
||||
fallbackDelay = d.networkFallbackDelay
|
||||
if !d.networkManager.AutoDetectInterface() {
|
||||
return nil, E.New("`route.auto_detect_interface` is require by `network_strategy`")
|
||||
}
|
||||
network := N.NetworkUDP
|
||||
if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
|
||||
network += "4"
|
||||
}
|
||||
return trackPacketConn(d.listenSerialInterfacePacket(ctx, d.udpListener, network, "", *strategy, interfaceType, fallbackInterfaceType, fallbackDelay))
|
||||
return trackPacketConn(d.listenSerialInterfacePacket(ctx, d.udpListener, network, "", strategy, interfaceType, fallbackInterfaceType, fallbackDelay))
|
||||
}
|
||||
|
||||
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
|
||||
return d.udpListener.ListenPacket(context.Background(), network, address)
|
||||
return trackPacketConn(d.udpListener.ListenPacket(context.Background(), network, address))
|
||||
}
|
||||
|
||||
func trackConn(conn net.Conn, err error) (net.Conn, error) {
|
||||
|
||||
@@ -40,7 +40,7 @@ func (d *DefaultDialer) dialParallelInterface(ctx context.Context, dialer net.Di
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case results <- dialResult{Conn: conn, primary: primary}:
|
||||
case results <- dialResult{Conn: conn}:
|
||||
case <-returned:
|
||||
conn.Close()
|
||||
}
|
||||
@@ -112,7 +112,7 @@ func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, d
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case results <- dialResult{Conn: conn, primary: primary}:
|
||||
case results <- dialResult{Conn: conn}:
|
||||
case <-returned:
|
||||
if primary && time.Since(startAt) <= fallbackDelay {
|
||||
resetFastFallback(time.Time{})
|
||||
@@ -180,19 +180,21 @@ func selectInterfaces(networkManager adapter.NetworkManager, strategy C.NetworkS
|
||||
for _, iif := range interfaces {
|
||||
if iif.Index == defaultIf.Index {
|
||||
primaryInterfaces = append(primaryInterfaces, iif)
|
||||
} else {
|
||||
fallbackInterfaces = append(fallbackInterfaces, iif)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
primaryInterfaces = common.Filter(interfaces, func(it adapter.NetworkInterface) bool {
|
||||
return common.Contains(interfaceType, it.Type)
|
||||
primaryInterfaces = common.Filter(interfaces, func(iif adapter.NetworkInterface) bool {
|
||||
return common.Contains(interfaceType, iif.Type)
|
||||
})
|
||||
}
|
||||
case C.NetworkStrategyHybrid:
|
||||
if len(interfaceType) == 0 {
|
||||
primaryInterfaces = interfaces
|
||||
} else {
|
||||
primaryInterfaces = common.Filter(interfaces, func(it adapter.NetworkInterface) bool {
|
||||
return common.Contains(interfaceType, it.Type)
|
||||
primaryInterfaces = common.Filter(interfaces, func(iif adapter.NetworkInterface) bool {
|
||||
return common.Contains(interfaceType, iif.Type)
|
||||
})
|
||||
}
|
||||
case C.NetworkStrategyFallback:
|
||||
@@ -201,25 +203,18 @@ func selectInterfaces(networkManager adapter.NetworkManager, strategy C.NetworkS
|
||||
for _, iif := range interfaces {
|
||||
if iif.Index == defaultIf.Index {
|
||||
primaryInterfaces = append(primaryInterfaces, iif)
|
||||
break
|
||||
} else {
|
||||
fallbackInterfaces = append(fallbackInterfaces, iif)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
primaryInterfaces = common.Filter(interfaces, func(it adapter.NetworkInterface) bool {
|
||||
return common.Contains(interfaceType, it.Type)
|
||||
})
|
||||
}
|
||||
if len(fallbackInterfaceType) == 0 {
|
||||
fallbackInterfaces = common.Filter(interfaces, func(it adapter.NetworkInterface) bool {
|
||||
return !common.Any(primaryInterfaces, func(iif adapter.NetworkInterface) bool {
|
||||
return it.Index == iif.Index
|
||||
})
|
||||
})
|
||||
} else {
|
||||
fallbackInterfaces = common.Filter(interfaces, func(iif adapter.NetworkInterface) bool {
|
||||
return common.Contains(fallbackInterfaceType, iif.Type)
|
||||
primaryInterfaces = common.Filter(interfaces, func(iif adapter.NetworkInterface) bool {
|
||||
return common.Contains(interfaceType, iif.Type)
|
||||
})
|
||||
}
|
||||
fallbackInterfaces = common.Filter(interfaces, func(iif adapter.NetworkInterface) bool {
|
||||
return common.Contains(fallbackInterfaceType, iif.Type)
|
||||
})
|
||||
}
|
||||
return primaryInterfaces, fallbackInterfaces
|
||||
}
|
||||
|
||||
@@ -13,13 +13,7 @@ import (
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func DialSerialNetwork(ctx context.Context, dialer N.Dialer, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||
if len(destinationAddresses) == 0 {
|
||||
if !destination.IsIP() {
|
||||
panic("invalid usage")
|
||||
}
|
||||
destinationAddresses = []netip.Addr{destination.Addr}
|
||||
}
|
||||
func DialSerialNetwork(ctx context.Context, dialer N.Dialer, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||
if parallelDialer, isParallel := dialer.(ParallelNetworkDialer); isParallel {
|
||||
return parallelDialer.DialParallelNetwork(ctx, network, destination, destinationAddresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||
}
|
||||
@@ -44,14 +38,7 @@ func DialSerialNetwork(ctx context.Context, dialer N.Dialer, network string, des
|
||||
return nil, E.Errors(errors...)
|
||||
}
|
||||
|
||||
func DialParallelNetwork(ctx context.Context, dialer ParallelInterfaceDialer, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, preferIPv6 bool, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||
if len(destinationAddresses) == 0 {
|
||||
if !destination.IsIP() {
|
||||
panic("invalid usage")
|
||||
}
|
||||
destinationAddresses = []netip.Addr{destination.Addr}
|
||||
}
|
||||
|
||||
func DialParallelNetwork(ctx context.Context, dialer ParallelInterfaceDialer, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, preferIPv6 bool, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||
if fallbackDelay == 0 {
|
||||
fallbackDelay = N.DefaultFallbackDelay
|
||||
}
|
||||
@@ -129,13 +116,7 @@ func DialParallelNetwork(ctx context.Context, dialer ParallelInterfaceDialer, ne
|
||||
}
|
||||
}
|
||||
|
||||
func ListenSerialNetworkPacket(ctx context.Context, dialer N.Dialer, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error) {
|
||||
if len(destinationAddresses) == 0 {
|
||||
if !destination.IsIP() {
|
||||
panic("invalid usage")
|
||||
}
|
||||
destinationAddresses = []netip.Addr{destination.Addr}
|
||||
}
|
||||
func ListenSerialNetworkPacket(ctx context.Context, dialer N.Dialer, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error) {
|
||||
if parallelDialer, isParallel := dialer.(ParallelNetworkDialer); isParallel {
|
||||
return parallelDialer.ListenSerialNetworkPacket(ctx, destination, destinationAddresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||
}
|
||||
|
||||
@@ -17,15 +17,16 @@ import (
|
||||
)
|
||||
|
||||
func New(ctx context.Context, options option.DialerOptions) (N.Dialer, error) {
|
||||
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
||||
if options.IsWireGuardListener {
|
||||
return NewDefault(ctx, options)
|
||||
return NewDefault(networkManager, options)
|
||||
}
|
||||
var (
|
||||
dialer N.Dialer
|
||||
err error
|
||||
)
|
||||
if options.Detour == "" {
|
||||
dialer, err = NewDefault(ctx, options)
|
||||
dialer, err = NewDefault(networkManager, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -36,6 +37,9 @@ func New(ctx context.Context, options option.DialerOptions) (N.Dialer, error) {
|
||||
}
|
||||
dialer = NewDetour(outboundManager, options.Detour)
|
||||
}
|
||||
if networkManager == nil {
|
||||
return NewDefault(networkManager, options)
|
||||
}
|
||||
if options.Detour == "" {
|
||||
router := service.FromContext[adapter.Router](ctx)
|
||||
if router != nil {
|
||||
@@ -54,10 +58,11 @@ func NewDirect(ctx context.Context, options option.DialerOptions) (ParallelInter
|
||||
if options.Detour != "" {
|
||||
return nil, E.New("`detour` is not supported in direct context")
|
||||
}
|
||||
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
||||
if options.IsWireGuardListener {
|
||||
return NewDefault(ctx, options)
|
||||
return NewDefault(networkManager, options)
|
||||
}
|
||||
dialer, err := NewDefault(ctx, options)
|
||||
dialer, err := NewDefault(networkManager, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -72,11 +77,11 @@ func NewDirect(ctx context.Context, options option.DialerOptions) (ParallelInter
|
||||
|
||||
type ParallelInterfaceDialer interface {
|
||||
N.Dialer
|
||||
DialParallelInterface(ctx context.Context, network string, destination M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error)
|
||||
ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error)
|
||||
DialParallelInterface(ctx context.Context, network string, destination M.Socksaddr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error)
|
||||
ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error)
|
||||
}
|
||||
|
||||
type ParallelNetworkDialer interface {
|
||||
DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error)
|
||||
ListenSerialNetworkPacket(ctx context.Context, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error)
|
||||
DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error)
|
||||
ListenSerialNetworkPacket(ctx context.Context, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error)
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ func (d *resolveDialer) ListenPacket(ctx context.Context, destination M.Socksadd
|
||||
return bufio.NewNATPacketConn(bufio.NewPacketConn(conn), M.SocksaddrFrom(destinationAddress, destination.Port), destination), nil
|
||||
}
|
||||
|
||||
func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context, network string, destination M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||
func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context, network string, destination M.Socksaddr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||
if !destination.IsFqdn() {
|
||||
return d.dialer.DialContext(ctx, network, destination)
|
||||
}
|
||||
@@ -134,7 +134,7 @@ func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context
|
||||
}
|
||||
}
|
||||
|
||||
func (d *resolveParallelNetworkDialer) ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error) {
|
||||
func (d *resolveParallelNetworkDialer) ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error) {
|
||||
if !destination.IsFqdn() {
|
||||
return d.dialer.ListenPacket(ctx, destination)
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common/ntp"
|
||||
)
|
||||
|
||||
type TimeServiceWrapper struct {
|
||||
ntp.TimeService
|
||||
}
|
||||
|
||||
func (w *TimeServiceWrapper) TimeFunc() func() time.Time {
|
||||
if w.TimeService == nil {
|
||||
return nil
|
||||
}
|
||||
return w.TimeService.TimeFunc()
|
||||
}
|
||||
|
||||
func (w *TimeServiceWrapper) Upstream() any {
|
||||
return w.TimeService
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
//go:build android && debug
|
||||
|
||||
package constant
|
||||
|
||||
// TODO: remove after fixed
|
||||
// https://github.com/golang/go/issues/68760
|
||||
|
||||
const FixAndroidStack = true
|
||||
@@ -1,5 +0,0 @@
|
||||
//go:build !(android && debug)
|
||||
|
||||
package constant
|
||||
|
||||
const FixAndroidStack = false
|
||||
@@ -1,7 +0,0 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
Hysterai2MasqueradeTypeFile = "file"
|
||||
Hysterai2MasqueradeTypeProxy = "proxy"
|
||||
Hysterai2MasqueradeTypeString = "string"
|
||||
)
|
||||
@@ -10,7 +10,6 @@ const (
|
||||
ProtocolDTLS = "dtls"
|
||||
ProtocolSSH = "ssh"
|
||||
ProtocolRDP = "rdp"
|
||||
ProtocolNTP = "ntp"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -9,6 +9,8 @@ const (
|
||||
TCPTimeout = 15 * time.Second
|
||||
ReadPayloadTimeout = 300 * time.Millisecond
|
||||
DNSTimeout = 10 * time.Second
|
||||
QUICTimeout = 30 * time.Second
|
||||
STUNTimeout = 15 * time.Second
|
||||
UDPTimeout = 5 * time.Minute
|
||||
DefaultURLTestInterval = 3 * time.Minute
|
||||
DefaultURLTestIdleTimeout = 30 * time.Minute
|
||||
@@ -17,18 +19,3 @@ const (
|
||||
FatalStopTimeout = 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,37 +2,10 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.11.0-beta.11
|
||||
#### 1.11.0-alpha.21
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
### 1.10.4
|
||||
|
||||
* 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.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
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
:material-alert: [masquerade](#masquerade)
|
||||
|
||||
### Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "hysteria2",
|
||||
"tag": "hy2-in",
|
||||
|
||||
... // Listen Fields
|
||||
...
|
||||
// Listen Fields
|
||||
|
||||
"up_mbps": 100,
|
||||
"down_mbps": 100,
|
||||
@@ -29,7 +21,7 @@ icon: material/alert-decagram
|
||||
],
|
||||
"ignore_client_bandwidth": false,
|
||||
"tls": {},
|
||||
"masquerade": "", // or {}
|
||||
"masquerade": "",
|
||||
"brutal_debug": false
|
||||
}
|
||||
```
|
||||
@@ -87,54 +79,14 @@ TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
||||
|
||||
#### masquerade
|
||||
|
||||
HTTP3 server behavior (URL string configuration) when authentication fails.
|
||||
HTTP3 server behavior when authentication fails.
|
||||
|
||||
| Scheme | Example | Description |
|
||||
|--------------|-------------------------|--------------------|
|
||||
| `file` | `file:///var/www` | As a file server |
|
||||
| `http/https` | `http://127.0.0.1:8080` | As a reverse proxy |
|
||||
|
||||
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.
|
||||
A 404 page will be returned if empty.
|
||||
|
||||
#### brutal_debug
|
||||
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
:material-alert: [masquerade](#masquerade)
|
||||
|
||||
### 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "hysteria2",
|
||||
"tag": "hy2-in",
|
||||
|
||||
... // 监听字段
|
||||
...
|
||||
// 监听字段
|
||||
|
||||
"up_mbps": 100,
|
||||
"down_mbps": 100,
|
||||
@@ -29,7 +21,7 @@ icon: material/alert-decagram
|
||||
],
|
||||
"ignore_client_bandwidth": false,
|
||||
"tls": {},
|
||||
"masquerade": "", // 或 {}
|
||||
"masquerade": "",
|
||||
"brutal_debug": false
|
||||
}
|
||||
```
|
||||
@@ -84,54 +76,14 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
||||
|
||||
#### masquerade
|
||||
|
||||
HTTP3 服务器认证失败时的行为 (URL 字符串配置)。
|
||||
HTTP3 服务器认证失败时的行为。
|
||||
|
||||
| Scheme | 示例 | 描述 |
|
||||
|--------------|-------------------------|---------|
|
||||
| `file` | `file:///var/www` | 作为文件服务器 |
|
||||
| `http/https` | `http://127.0.0.1:8080` | 作为反向代理 |
|
||||
|
||||
如果 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
|
||||
|
||||
固定响应内容。
|
||||
如果为空,则返回 404 页。
|
||||
|
||||
#### brutal_debug
|
||||
|
||||
|
||||
@@ -88,13 +88,13 @@ icon: material/alert-decagram
|
||||
0
|
||||
],
|
||||
"include_uid_range": [
|
||||
"1000:99999"
|
||||
"1000-99999"
|
||||
],
|
||||
"exclude_uid": [
|
||||
1000
|
||||
],
|
||||
"exclude_uid_range": [
|
||||
"1000:99999"
|
||||
"1000-99999"
|
||||
],
|
||||
"include_android_user": [
|
||||
0,
|
||||
|
||||
@@ -88,13 +88,13 @@ icon: material/alert-decagram
|
||||
0
|
||||
],
|
||||
"include_uid_range": [
|
||||
"1000:99999"
|
||||
"1000-99999"
|
||||
],
|
||||
"exclude_uid": [
|
||||
1000
|
||||
],
|
||||
"exclude_uid_range": [
|
||||
"1000:99999"
|
||||
"1000-99999"
|
||||
],
|
||||
"include_android_user": [
|
||||
0,
|
||||
|
||||
@@ -4,8 +4,8 @@ icon: material/alert-decagram
|
||||
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
:material-delete-clock: [override_address](#override_address)
|
||||
:material-delete-clock: [override_port](#override_port)
|
||||
:material-alert-decagram: [override_address](#override_address)
|
||||
:material-alert-decagram: [override_port](#override_port)
|
||||
|
||||
`direct` outbound send requests directly.
|
||||
|
||||
|
||||
@@ -41,8 +41,7 @@ See `route-options` fields below.
|
||||
"network_strategy": "",
|
||||
"fallback_delay": "",
|
||||
"udp_disable_domain_unmapping": false,
|
||||
"udp_connect": false,
|
||||
"udp_timeout": ""
|
||||
"udp_connect": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -87,28 +86,6 @@ 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.
|
||||
|
||||
#### 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
|
||||
|
||||
```json
|
||||
|
||||
@@ -37,8 +37,7 @@ icon: material/new-box
|
||||
"network_strategy": "",
|
||||
"fallback_delay": "",
|
||||
"udp_disable_domain_unmapping": false,
|
||||
"udp_connect": false,
|
||||
"udp_timeout": ""
|
||||
"udp_connect": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -85,28 +84,6 @@ icon: material/new-box
|
||||
|
||||
如果启用,将尝试将 UDP 连接 connect 到目标而不是 listen。
|
||||
|
||||
#### udp_timeout
|
||||
|
||||
UDP 连接超时时间。
|
||||
|
||||
设置比入站 UDP 超时更大的值将无效。
|
||||
|
||||
已探测协议连接的默认值:
|
||||
|
||||
| 超时 | 协议 |
|
||||
|-------|----------------------|
|
||||
| `10s` | `dns`, `ntp`, `stun` |
|
||||
| `30s` | `quic`, `dtls` |
|
||||
|
||||
如果没有探测到协议,以下端口将默认识别为协议:
|
||||
|
||||
| 端口 | 协议 |
|
||||
|------|--------|
|
||||
| 53 | `dns` |
|
||||
| 123 | `ntp` |
|
||||
| 443 | `quic` |
|
||||
| 3478 | `stun` |
|
||||
|
||||
### reject
|
||||
|
||||
```json
|
||||
|
||||
@@ -32,7 +32,7 @@ func groupRouter(server *Server) http.Handler {
|
||||
|
||||
func getGroups(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
groups := common.Map(common.Filter(server.outbound.Outbounds(), func(it adapter.Outbound) bool {
|
||||
groups := common.Map(common.Filter(server.outboundManager.Outbounds(), func(it adapter.Outbound) bool {
|
||||
_, isGroup := it.(adapter.OutboundGroup)
|
||||
return isGroup
|
||||
}), 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)
|
||||
} else {
|
||||
outbounds := common.FilterNotNil(common.Map(outboundGroup.All(), func(it string) adapter.Outbound {
|
||||
itOutbound, _ := server.outbound.Outbound(it)
|
||||
itOutbound, _ := server.outboundManager.Outbound(it)
|
||||
return itOutbound
|
||||
}))
|
||||
b, _ := batch.New(ctx, batch.WithConcurrencyNum[any](10))
|
||||
@@ -100,7 +100,7 @@ func getGroupDelay(server *Server) func(w http.ResponseWriter, r *http.Request)
|
||||
continue
|
||||
}
|
||||
checked[realTag] = true
|
||||
p, loaded := server.outbound.Outbound(realTag)
|
||||
p, loaded := server.outboundManager.Outbound(realTag)
|
||||
if !loaded {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -18,19 +18,17 @@ func configRouter(server *Server, logFactory log.Factory) http.Handler {
|
||||
}
|
||||
|
||||
type configSchema struct {
|
||||
Port int `json:"port"`
|
||||
SocksPort int `json:"socks-port"`
|
||||
RedirPort int `json:"redir-port"`
|
||||
TProxyPort int `json:"tproxy-port"`
|
||||
MixedPort int `json:"mixed-port"`
|
||||
AllowLan bool `json:"allow-lan"`
|
||||
BindAddress string `json:"bind-address"`
|
||||
Mode string `json:"mode"`
|
||||
// sing-box added
|
||||
ModeList []string `json:"mode-list"`
|
||||
LogLevel string `json:"log-level"`
|
||||
IPv6 bool `json:"ipv6"`
|
||||
Tun map[string]any `json:"tun"`
|
||||
Port int `json:"port"`
|
||||
SocksPort int `json:"socks-port"`
|
||||
RedirPort int `json:"redir-port"`
|
||||
TProxyPort int `json:"tproxy-port"`
|
||||
MixedPort int `json:"mixed-port"`
|
||||
AllowLan bool `json:"allow-lan"`
|
||||
BindAddress string `json:"bind-address"`
|
||||
Mode string `json:"mode"`
|
||||
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) {
|
||||
@@ -43,7 +41,6 @@ func getConfigs(server *Server, logFactory log.Factory) func(w http.ResponseWrit
|
||||
}
|
||||
render.JSON(w, r, &configSchema{
|
||||
Mode: server.mode,
|
||||
ModeList: server.modeList,
|
||||
BindAddress: "*",
|
||||
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 http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
name := r.Context().Value(CtxKeyProxyName).(string)
|
||||
proxy, exist := server.outbound.Outbound(name)
|
||||
proxy, exist := server.outboundManager.Outbound(name)
|
||||
if !exist {
|
||||
render.Status(r, http.StatusNotFound)
|
||||
render.JSON(w, r, ErrNotFound)
|
||||
@@ -86,14 +86,9 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
|
||||
func getProxies(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var proxyMap badjson.JSONObject
|
||||
outbounds := common.Filter(server.outbound.Outbounds(), func(detour adapter.Outbound) bool {
|
||||
outbounds := common.Filter(server.outboundManager.Outbounds(), func(detour adapter.Outbound) bool {
|
||||
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))
|
||||
|
||||
@@ -105,7 +100,7 @@ func getProxies(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
||||
allProxies = append(allProxies, detour.Tag())
|
||||
}
|
||||
|
||||
defaultTag := server.outbound.Default().Tag()
|
||||
defaultTag := server.outboundManager.Default().Tag()
|
||||
|
||||
sort.SliceStable(allProxies, func(i, j int) bool {
|
||||
return allProxies[i] == defaultTag
|
||||
|
||||
@@ -40,17 +40,16 @@ func init() {
|
||||
var _ adapter.ClashServer = (*Server)(nil)
|
||||
|
||||
type Server struct {
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
outbound adapter.OutboundManager
|
||||
endpoint adapter.EndpointManager
|
||||
logger log.Logger
|
||||
httpServer *http.Server
|
||||
trafficManager *trafficontrol.Manager
|
||||
urlTestHistory *urltest.HistoryStorage
|
||||
mode string
|
||||
modeList []string
|
||||
modeUpdateHook chan<- struct{}
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
outboundManager adapter.OutboundManager
|
||||
logger log.Logger
|
||||
httpServer *http.Server
|
||||
trafficManager *trafficontrol.Manager
|
||||
urlTestHistory *urltest.HistoryStorage
|
||||
mode string
|
||||
modeList []string
|
||||
modeUpdateHook chan<- struct{}
|
||||
|
||||
externalController bool
|
||||
externalUI string
|
||||
@@ -62,11 +61,10 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op
|
||||
trafficManager := trafficontrol.NewManager()
|
||||
chiRouter := chi.NewRouter()
|
||||
s := &Server{
|
||||
ctx: ctx,
|
||||
router: service.FromContext[adapter.Router](ctx),
|
||||
outbound: service.FromContext[adapter.OutboundManager](ctx),
|
||||
endpoint: service.FromContext[adapter.EndpointManager](ctx),
|
||||
logger: logFactory.NewLogger("clash-api"),
|
||||
ctx: ctx,
|
||||
router: service.FromContext[adapter.Router](ctx),
|
||||
outboundManager: service.FromContext[adapter.OutboundManager](ctx),
|
||||
logger: logFactory.NewLogger("clash-api"),
|
||||
httpServer: &http.Server{
|
||||
Addr: options.ExternalController,
|
||||
Handler: chiRouter,
|
||||
@@ -128,8 +126,11 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op
|
||||
if options.ExternalUI != "" {
|
||||
s.externalUI = filemanager.BasePath(ctx, os.ExpandEnv(options.ExternalUI))
|
||||
chiRouter.Group(func(r chi.Router) {
|
||||
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusMovedPermanently).ServeHTTP)
|
||||
r.Handle("/ui/*", http.StripPrefix("/ui/", http.FileServer(http.Dir(s.externalUI))))
|
||||
fs := http.StripPrefix("/ui", http.FileServer(http.Dir(s.externalUI)))
|
||||
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusTemporaryRedirect).ServeHTTP)
|
||||
r.Get("/ui/*", func(w http.ResponseWriter, r *http.Request) {
|
||||
fs.ServeHTTP(w, r)
|
||||
})
|
||||
})
|
||||
}
|
||||
return s, nil
|
||||
@@ -241,11 +242,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 {
|
||||
return trafficontrol.NewTCPTracker(conn, s.trafficManager, metadata, s.outbound, matchedRule, matchOutbound)
|
||||
return trafficontrol.NewTCPTracker(conn, s.trafficManager, metadata, s.outboundManager, matchedRule, matchOutbound)
|
||||
}
|
||||
|
||||
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.outbound, matchedRule, matchOutbound)
|
||||
return trafficontrol.NewUDPTracker(conn, s.trafficManager, metadata, s.outboundManager, matchedRule, matchOutbound)
|
||||
}
|
||||
|
||||
func authentication(serverSecret string) func(next http.Handler) http.Handler {
|
||||
@@ -320,29 +321,27 @@ func traffic(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter,
|
||||
tick := time.NewTicker(time.Second)
|
||||
defer tick.Stop()
|
||||
buf := &bytes.Buffer{}
|
||||
uploadTotal, downloadTotal := trafficManager.Total()
|
||||
var err error
|
||||
for range tick.C {
|
||||
buf.Reset()
|
||||
uploadTotalNew, downloadTotalNew := trafficManager.Total()
|
||||
err := json.NewEncoder(buf).Encode(Traffic{
|
||||
Up: uploadTotalNew - uploadTotal,
|
||||
Down: downloadTotalNew - downloadTotal,
|
||||
})
|
||||
if err != nil {
|
||||
up, down := trafficManager.Now()
|
||||
if err := json.NewEncoder(buf).Encode(Traffic{
|
||||
Up: up,
|
||||
Down: down,
|
||||
}); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if conn == nil {
|
||||
_, err = w.Write(buf.Bytes())
|
||||
w.(http.Flusher).Flush()
|
||||
} else {
|
||||
err = wsutil.WriteServerText(conn, buf.Bytes())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
uploadTotal = uploadTotalNew
|
||||
downloadTotal = downloadTotalNew
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,13 +44,13 @@ func (s *Server) downloadExternalUI() error {
|
||||
s.logger.Info("downloading external ui")
|
||||
var detour adapter.Outbound
|
||||
if s.externalUIDownloadDetour != "" {
|
||||
outbound, loaded := s.outbound.Outbound(s.externalUIDownloadDetour)
|
||||
outbound, loaded := s.outboundManager.Outbound(s.externalUIDownloadDetour)
|
||||
if !loaded {
|
||||
return E.New("detour outbound not found: ", s.externalUIDownloadDetour)
|
||||
}
|
||||
detour = outbound
|
||||
} else {
|
||||
outbound := s.outbound.Default()
|
||||
outbound := s.outboundManager.Default()
|
||||
detour = outbound
|
||||
}
|
||||
httpClient := &http.Client{
|
||||
|
||||
@@ -16,18 +16,30 @@ import (
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
uploadTemp atomic.Int64
|
||||
downloadTemp atomic.Int64
|
||||
uploadBlip atomic.Int64
|
||||
downloadBlip atomic.Int64
|
||||
uploadTotal atomic.Int64
|
||||
downloadTotal atomic.Int64
|
||||
|
||||
connections compatible.Map[uuid.UUID, Tracker]
|
||||
closedConnectionsAccess sync.Mutex
|
||||
closedConnections list.List[TrackerMetadata]
|
||||
ticker *time.Ticker
|
||||
done chan struct{}
|
||||
// process *process.Process
|
||||
memory uint64
|
||||
}
|
||||
|
||||
func NewManager() *Manager {
|
||||
return &Manager{}
|
||||
manager := &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) {
|
||||
@@ -49,13 +61,19 @@ func (m *Manager) Leave(c Tracker) {
|
||||
}
|
||||
|
||||
func (m *Manager) PushUploaded(size int64) {
|
||||
m.uploadTemp.Add(size)
|
||||
m.uploadTotal.Add(size)
|
||||
}
|
||||
|
||||
func (m *Manager) PushDownloaded(size int64) {
|
||||
m.downloadTemp.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) {
|
||||
return m.uploadTotal.Load(), m.downloadTotal.Load()
|
||||
}
|
||||
@@ -109,10 +127,36 @@ func (m *Manager) Snapshot() *Snapshot {
|
||||
}
|
||||
|
||||
func (m *Manager) ResetStatistic() {
|
||||
m.uploadTemp.Store(0)
|
||||
m.uploadBlip.Store(0)
|
||||
m.uploadTotal.Store(0)
|
||||
m.downloadTemp.Store(0)
|
||||
m.downloadBlip.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 {
|
||||
Download int64
|
||||
Upload int64
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package deprecated
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sagernet/sing-box/common/badversion"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/locale"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
|
||||
"golang.org/x/mod/semver"
|
||||
@@ -37,9 +34,15 @@ func (n Note) Impending() bool {
|
||||
|
||||
func (n Note) Message() string {
|
||||
if n.MigrationLink != "" {
|
||||
return fmt.Sprintf(locale.Current().DeprecatedMessage, n.Description, n.DeprecatedVersion, n.ScheduledVersion)
|
||||
return F.ToString(
|
||||
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 fmt.Sprintf(locale.Current().DeprecatedMessageNoLink, n.Description, n.DeprecatedVersion, n.ScheduledVersion)
|
||||
return F.ToString(
|
||||
n.Description, " is deprecated in sing-box ", n.DeprecatedVersion,
|
||||
" and will be removed in sing-box ", n.ScheduledVersion, ".",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
@@ -114,24 +113,11 @@ func (c *CommandClient) Connect() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if C.FixAndroidStack {
|
||||
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())
|
||||
}
|
||||
}
|
||||
c.handler.Connected()
|
||||
c.handler.InitializeClashMode(newIterator(modeList), currentMode)
|
||||
if len(modeList) == 0 {
|
||||
conn.Close()
|
||||
c.handler.Disconnected(os.ErrInvalid.Error())
|
||||
return nil
|
||||
}
|
||||
go c.handleModeConn(conn)
|
||||
|
||||
@@ -33,6 +33,7 @@ func (s *CommandServer) readStatus() StatusMessage {
|
||||
if s.service != nil {
|
||||
message.TrafficAvailable = true
|
||||
trafficManager := s.service.clashServer.(*clashapi.Server).TrafficManager()
|
||||
message.Uplink, message.Downlink = trafficManager.Now()
|
||||
message.UplinkTotal, message.DownlinkTotal = trafficManager.Total()
|
||||
message.ConnectionsIn = int32(trafficManager.ConnectionsLen())
|
||||
}
|
||||
@@ -49,11 +50,8 @@ func (s *CommandServer) handleStatusConn(conn net.Conn) error {
|
||||
ticker := time.NewTicker(time.Duration(interval))
|
||||
defer ticker.Stop()
|
||||
ctx := connKeepAlive(conn)
|
||||
status := s.readStatus()
|
||||
uploadTotal := status.UplinkTotal
|
||||
downloadTotal := status.DownlinkTotal
|
||||
for {
|
||||
err = binary.Write(conn, binary.BigEndian, status)
|
||||
err = binary.Write(conn, binary.BigEndian, s.readStatus())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -62,13 +60,6 @@ func (s *CommandServer) handleStatusConn(conn net.Conn) error {
|
||||
return ctx.Err()
|
||||
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
|
||||
}
|
||||
|
||||
func FormatConfig(configContent string) (*StringBox, error) {
|
||||
func FormatConfig(configContent string) (string, error) {
|
||||
options, err := parseConfig(box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry()), configContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
encoder := json.NewEncoder(&buffer)
|
||||
encoder.SetIndent("", " ")
|
||||
err = encoder.Encode(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
return wrapString(buffer.String()), nil
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
||||
@@ -50,7 +50,8 @@ type HTTPRequest interface {
|
||||
}
|
||||
|
||||
type HTTPResponse interface {
|
||||
GetContent() (*StringBox, error)
|
||||
GetContent() ([]byte, error)
|
||||
GetContentString() (string, error)
|
||||
WriteTo(path string) error
|
||||
}
|
||||
|
||||
@@ -209,22 +210,27 @@ type httpResponse struct {
|
||||
}
|
||||
|
||||
func (h *httpResponse) errorString() string {
|
||||
content, err := h.GetContent()
|
||||
content, err := h.GetContentString()
|
||||
if err != nil {
|
||||
return fmt.Sprint("HTTP ", h.Status)
|
||||
}
|
||||
return fmt.Sprint("HTTP ", h.Status, ": ", content)
|
||||
}
|
||||
|
||||
func (h *httpResponse) GetContent() (*StringBox, error) {
|
||||
func (h *httpResponse) GetContent() ([]byte, error) {
|
||||
h.getContentOnce.Do(func() {
|
||||
defer h.Body.Close()
|
||||
h.content, h.contentError = io.ReadAll(h.Body)
|
||||
})
|
||||
if h.contentError != nil {
|
||||
return nil, h.contentError
|
||||
return h.content, h.contentError
|
||||
}
|
||||
|
||||
func (h *httpResponse) GetContentString() (string, error) {
|
||||
content, err := h.GetContent()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return wrapString(string(h.content)), nil
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
func (h *httpResponse) WriteTo(path string) error {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -56,14 +55,6 @@ func (m *platformDefaultInterfaceMonitor) UnregisterCallback(element *list.Eleme
|
||||
}
|
||||
|
||||
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.isConstrained = isConstrained
|
||||
err := m.networkManager.UpdateInterfaces()
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
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,36 +81,23 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
||||
}
|
||||
|
||||
func (s *BoxService) Start() error {
|
||||
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()
|
||||
}
|
||||
return s.instance.Start()
|
||||
}
|
||||
|
||||
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.urlTestHistoryStorage.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
|
||||
}
|
||||
return s.instance.Close()
|
||||
}
|
||||
|
||||
func (s *BoxService) NeedWIFIState() bool {
|
||||
|
||||
@@ -13,12 +13,12 @@ func ClearServiceError() {
|
||||
os.Remove(serviceErrorPath())
|
||||
}
|
||||
|
||||
func ReadServiceError() (*StringBox, error) {
|
||||
func ReadServiceError() (string, error) {
|
||||
data, err := os.ReadFile(serviceErrorPath())
|
||||
if err == nil {
|
||||
os.Remove(serviceErrorPath())
|
||||
}
|
||||
return wrapString(string(data)), err
|
||||
return string(data), err
|
||||
}
|
||||
|
||||
func WriteServiceError(message string) error {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/common/humanize"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/locale"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
)
|
||||
|
||||
@@ -54,10 +53,6 @@ func SetupWithUsername(basePath string, workingPath string, tempPath string, use
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetLocale(localeId string) {
|
||||
locale.Set(localeId)
|
||||
}
|
||||
|
||||
func Version() string {
|
||||
return C.Version
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
type TunOptions interface {
|
||||
GetInet4Address() RoutePrefixIterator
|
||||
GetInet6Address() RoutePrefixIterator
|
||||
GetDNSServerAddress() (*StringBox, error)
|
||||
GetDNSServerAddress() (string, error)
|
||||
GetMTU() int32
|
||||
GetAutoRoute() bool
|
||||
GetStrictRoute() bool
|
||||
@@ -89,11 +89,11 @@ func (o *tunOptions) GetInet6Address() RoutePrefixIterator {
|
||||
return mapRoutePrefix(o.Inet6Address)
|
||||
}
|
||||
|
||||
func (o *tunOptions) GetDNSServerAddress() (*StringBox, error) {
|
||||
func (o *tunOptions) GetDNSServerAddress() (string, error) {
|
||||
if len(o.Inet4Address) == 0 || o.Inet4Address[0].Bits() == 32 {
|
||||
return nil, E.New("need one more IPv4 address for DNS hijacking")
|
||||
return "", E.New("need one more IPv4 address for DNS hijacking")
|
||||
}
|
||||
return wrapString(o.Inet4Address[0].Addr().Next().String()), nil
|
||||
return o.Inet4Address[0].Addr().Next().String(), nil
|
||||
}
|
||||
|
||||
func (o *tunOptions) GetMTU() int32 {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package locale
|
||||
|
||||
var (
|
||||
localeRegistry = make(map[string]*Locale)
|
||||
current = defaultLocal
|
||||
)
|
||||
|
||||
type Locale struct {
|
||||
// deprecated messages for graphical clients
|
||||
DeprecatedMessage string
|
||||
DeprecatedMessageNoLink string
|
||||
}
|
||||
|
||||
var defaultLocal = &Locale{
|
||||
DeprecatedMessage: "%s is deprecated in sing-box %s and will be removed in sing-box %s please checkout documentation for migration.",
|
||||
DeprecatedMessageNoLink: "%s is deprecated in sing-box %s and will be removed in sing-box %s.",
|
||||
}
|
||||
|
||||
func Current() *Locale {
|
||||
return current
|
||||
}
|
||||
|
||||
func Set(localeId string) bool {
|
||||
locale, loaded := localeRegistry[localeId]
|
||||
if !loaded {
|
||||
return false
|
||||
}
|
||||
current = locale
|
||||
return true
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package locale
|
||||
|
||||
var warningMessageForEndUsers = "\n\n如果您不明白此消息意味着什么:您的配置文件已过时,且将很快不可用。请联系您的配置提供者以更新配置。"
|
||||
|
||||
func init() {
|
||||
localeRegistry["zh_CN"] = &Locale{
|
||||
DeprecatedMessage: "%s 已在 sing-box %s 中被弃用,且将在 sing-box %s 中被移除,请参阅迁移指南。" + warningMessageForEndUsers,
|
||||
DeprecatedMessageNoLink: "%s 已在 sing-box %s 中被弃用,且将在 sing-box %s 中被移除。" + warningMessageForEndUsers,
|
||||
}
|
||||
}
|
||||
14
go.mod
14
go.mod
@@ -4,13 +4,11 @@ go 1.20
|
||||
|
||||
require (
|
||||
github.com/caddyserver/certmagic v0.20.0
|
||||
github.com/cidertool/asc-go v0.5.1
|
||||
github.com/cloudflare/circl v1.3.7
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/go-chi/chi/v5 v5.1.0
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/gofrs/uuid/v5 v5.3.0
|
||||
github.com/google/go-querystring v1.0.0
|
||||
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2
|
||||
github.com/libdns/alidns v1.0.3
|
||||
github.com/libdns/cloudflare v0.1.1
|
||||
@@ -25,20 +23,20 @@ require (
|
||||
github.com/sagernet/fswatch v0.1.1
|
||||
github.com/sagernet/gomobile v0.1.4
|
||||
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
|
||||
github.com/sagernet/quic-go v0.48.2-beta.1
|
||||
github.com/sagernet/quic-go v0.48.1-beta.1
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.6.0-beta.8
|
||||
github.com/sagernet/sing-dns v0.4.0-beta.1
|
||||
github.com/sagernet/sing v0.6.0-alpha.18
|
||||
github.com/sagernet/sing-dns v0.4.0-alpha.3
|
||||
github.com/sagernet/sing-mux v0.3.0-alpha.1
|
||||
github.com/sagernet/sing-quic v0.4.0-alpha.4
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2
|
||||
github.com/sagernet/sing-tun v0.6.0-beta.6
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.14
|
||||
github.com/sagernet/sing-vmess v0.2.0-beta.1
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||
github.com/sagernet/utls v1.6.7
|
||||
github.com/sagernet/wireguard-go v0.0.1-beta.5
|
||||
github.com/sagernet/wireguard-go v0.0.1-beta.4
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
@@ -60,9 +58,7 @@ require (
|
||||
require (
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
|
||||
32
go.sum
32
go.sum
@@ -4,10 +4,6 @@ github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sx
|
||||
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
|
||||
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
|
||||
github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc=
|
||||
github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/cidertool/asc-go v0.5.1 h1:KYki2Y8IXJMOkOXy9y1sdr8tz6IdW2ti770K4bk7WY0=
|
||||
github.com/cidertool/asc-go v0.5.1/go.mod h1:LyrZWU7DeCh8cWrFwXcpl93ixRUUL2aEZV7/0h07FxA=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
@@ -16,8 +12,6 @@ github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbe
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU=
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
||||
@@ -40,8 +34,6 @@ github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
|
||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
||||
@@ -113,15 +105,15 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
||||
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
||||
github.com/sagernet/quic-go v0.48.2-beta.1 h1:W0plrLWa1XtOWDTdX3CJwxmQuxkya12nN5BRGZ87kEg=
|
||||
github.com/sagernet/quic-go v0.48.2-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k=
|
||||
github.com/sagernet/quic-go v0.48.1-beta.1 h1:ElPaV5yzlXIKZpqFMAcUGax6vddi3zt4AEpT94Z0vwo=
|
||||
github.com/sagernet/quic-go v0.48.1-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/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||
github.com/sagernet/sing v0.6.0-beta.8 h1:PoxDdN7y8D4oImT3cQ05Sq1ZYnYsJberkUkIEHIGwWE=
|
||||
github.com/sagernet/sing v0.6.0-beta.8/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-dns v0.4.0-beta.1 h1:W1XkdhigwxDOMgMDVB+9kdomCpb7ExsZfB4acPcTZFY=
|
||||
github.com/sagernet/sing-dns v0.4.0-beta.1/go.mod h1:8wuFcoFkWM4vJuQyg8e97LyvDwe0/Vl7G839WLcKDs8=
|
||||
github.com/sagernet/sing v0.6.0-alpha.18 h1:ih4CurU8KvbhfagYjSqVrE2LR0oBSXSZTNH2sAGPGiM=
|
||||
github.com/sagernet/sing v0.6.0-alpha.18/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-alpha.3/go.mod h1:9LHcYKg2bGQpbtXrfNbopz8ok/zBK9ljiI2kmFG9JKg=
|
||||
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-quic v0.4.0-alpha.4 h1:P9xAx3nIfcqb9M8jfgs0uLm+VxCcaY++FCqaBfHY3dQ=
|
||||
@@ -132,16 +124,18 @@ 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-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-tun v0.6.0-beta.6 h1:xaIHoH78MqTSvZqQ4SQto8pC1A+X4qXReDRNaC8DQeI=
|
||||
github.com/sagernet/sing-tun v0.6.0-beta.6/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.14 h1:0nE66HdC6nBSOaUG0CEV5rwB5Te3Gts9buVOPvWrGT4=
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.14/go.mod h1:xvZlEl1EGBbQeshv4UXmG7hA3f0ngFjpdCIYk308vfg=
|
||||
github.com/sagernet/sing-vmess v0.1.13-0.20241123134803-8b806fd4b087 h1:p92kbwAIm5Is8V+fK6IB61AZs/nfWoyxxJeib2Dh2o0=
|
||||
github.com/sagernet/sing-vmess v0.1.13-0.20241123134803-8b806fd4b087/go.mod h1:fLyE1emIcvQ5DV8reFWnufquZ7MkCSYM5ThodsR9NrQ=
|
||||
github.com/sagernet/sing-vmess v0.2.0-beta.1 h1:5sXQ23uwNlZuDvygzi0dFtnG0Csm/SNqTjAHXJkpuj4=
|
||||
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/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/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM=
|
||||
github.com/sagernet/wireguard-go v0.0.1-beta.5 h1:aBEsxJUMEONwOZqKPIkuAcv4zJV5p6XlzEN04CF0FXc=
|
||||
github.com/sagernet/wireguard-go v0.0.1-beta.5/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo=
|
||||
github.com/sagernet/wireguard-go v0.0.1-beta.4 h1:8uyM5fxfEXdu4RH05uOK+v25i3lTNdCYMPSAUJ14FnI=
|
||||
github.com/sagernet/wireguard-go v0.0.1-beta.4/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/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
@@ -203,8 +197,6 @@ 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.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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=
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
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 {
|
||||
ListenOptions
|
||||
UpMbps int `json:"up_mbps,omitempty"`
|
||||
@@ -18,8 +8,8 @@ type Hysteria2InboundOptions struct {
|
||||
Users []Hysteria2User `json:"users,omitempty"`
|
||||
IgnoreClientBandwidth bool `json:"ignore_client_bandwidth,omitempty"`
|
||||
InboundTLSOptionsContainer
|
||||
Masquerade *Hysteria2Masquerade `json:"masquerade,omitempty"`
|
||||
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
||||
Masquerade string `json:"masquerade,omitempty"`
|
||||
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
||||
}
|
||||
|
||||
type Hysteria2Obfs struct {
|
||||
@@ -32,82 +22,6 @@ type Hysteria2User struct {
|
||||
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 {
|
||||
DialerOptions
|
||||
ServerOptions
|
||||
|
||||
@@ -65,24 +65,25 @@ type DialerOptionsWrapper interface {
|
||||
}
|
||||
|
||||
type DialerOptions struct {
|
||||
Detour string `json:"detour,omitempty"`
|
||||
BindInterface string `json:"bind_interface,omitempty"`
|
||||
Inet4BindAddress *badoption.Addr `json:"inet4_bind_address,omitempty"`
|
||||
Inet6BindAddress *badoption.Addr `json:"inet6_bind_address,omitempty"`
|
||||
ProtectPath string `json:"protect_path,omitempty"`
|
||||
RoutingMark FwMark `json:"routing_mark,omitempty"`
|
||||
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
||||
ConnectTimeout badoption.Duration `json:"connect_timeout,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||
UDPFragmentDefault bool `json:"-"`
|
||||
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
||||
NetworkStrategy *NetworkStrategy `json:"network_strategy,omitempty"`
|
||||
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||
FallbackNetworkType badoption.Listable[InterfaceType] `json:"fallback_network_type,omitempty"`
|
||||
FallbackDelay badoption.Duration `json:"fallback_delay,omitempty"`
|
||||
IsWireGuardListener bool `json:"-"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
BindInterface string `json:"bind_interface,omitempty"`
|
||||
Inet4BindAddress *badoption.Addr `json:"inet4_bind_address,omitempty"`
|
||||
Inet6BindAddress *badoption.Addr `json:"inet6_bind_address,omitempty"`
|
||||
ProtectPath string `json:"protect_path,omitempty"`
|
||||
RoutingMark FwMark `json:"routing_mark,omitempty"`
|
||||
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
||||
ConnectTimeout badoption.Duration `json:"connect_timeout,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||
UDPFragmentDefault bool `json:"-"`
|
||||
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
||||
NetworkStrategy NetworkStrategy `json:"network_strategy,omitempty"`
|
||||
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||
FallbackNetworkType badoption.Listable[InterfaceType] `json:"fallback_network_type,omitempty"`
|
||||
FallbackDelay badoption.Duration `json:"fallback_delay,omitempty"`
|
||||
NetworkFallbackDelay badoption.Duration `json:"network_fallback_delay,omitempty"`
|
||||
IsWireGuardListener bool `json:"-"`
|
||||
}
|
||||
|
||||
func (o *DialerOptions) TakeDialerOptions() DialerOptions {
|
||||
|
||||
@@ -12,8 +12,8 @@ type RouteOptions struct {
|
||||
AutoDetectInterface bool `json:"auto_detect_interface,omitempty"`
|
||||
OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"`
|
||||
DefaultInterface string `json:"default_interface,omitempty"`
|
||||
DefaultMark FwMark `json:"default_mark,omitempty"`
|
||||
DefaultNetworkStrategy *NetworkStrategy `json:"default_network_strategy,omitempty"`
|
||||
DefaultMark uint32 `json:"default_mark,omitempty"`
|
||||
DefaultNetworkStrategy NetworkStrategy `json:"default_network_strategy,omitempty"`
|
||||
DefaultNetworkType badoption.Listable[InterfaceType] `json:"default_network_type,omitempty"`
|
||||
DefaultFallbackNetworkType badoption.Listable[InterfaceType] `json:"default_fallback_network_type,omitempty"`
|
||||
DefaultFallbackDelay badoption.Duration `json:"default_fallback_delay,omitempty"`
|
||||
|
||||
@@ -145,12 +145,11 @@ type RawRouteOptionsActionOptions struct {
|
||||
OverrideAddress string `json:"override_address,omitempty"`
|
||||
OverridePort uint16 `json:"override_port,omitempty"`
|
||||
|
||||
NetworkStrategy *NetworkStrategy `json:"network_strategy,omitempty"`
|
||||
FallbackDelay uint32 `json:"fallback_delay,omitempty"`
|
||||
NetworkStrategy NetworkStrategy `json:"network_strategy,omitempty"`
|
||||
FallbackDelay uint32 `json:"fallback_delay,omitempty"`
|
||||
|
||||
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
||||
UDPConnect bool `json:"udp_connect,omitempty"`
|
||||
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
|
||||
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
||||
UDPConnect bool `json:"udp_connect,omitempty"`
|
||||
}
|
||||
|
||||
type RouteOptionsActionOptions RawRouteOptionsActionOptions
|
||||
|
||||
@@ -14,7 +14,7 @@ type WireGuardEndpointOptions struct {
|
||||
PrivateKey string `json:"private_key"`
|
||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||
Peers []WireGuardPeer `json:"peers,omitempty"`
|
||||
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
|
||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
||||
Workers int `json:"workers,omitempty"`
|
||||
DialerOptions
|
||||
}
|
||||
|
||||
@@ -97,7 +97,6 @@ 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) {
|
||||
metadata.Inbound = i.Tag()
|
||||
metadata.InboundType = i.Type()
|
||||
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr())
|
||||
switch i.overrideOption {
|
||||
case 1:
|
||||
metadata.Destination = i.overrideDestination
|
||||
@@ -108,9 +107,7 @@ func (i *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata a
|
||||
case 3:
|
||||
metadata.Destination.Port = i.overrideDestination.Port
|
||||
}
|
||||
if i.overrideOption != 0 {
|
||||
i.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
}
|
||||
i.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
i.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
|
||||
@@ -32,12 +32,16 @@ var (
|
||||
|
||||
type Outbound struct {
|
||||
outbound.Adapter
|
||||
logger logger.ContextLogger
|
||||
dialer dialer.ParallelInterfaceDialer
|
||||
domainStrategy dns.DomainStrategy
|
||||
fallbackDelay time.Duration
|
||||
overrideOption int
|
||||
overrideDestination M.Socksaddr
|
||||
logger logger.ContextLogger
|
||||
dialer dialer.ParallelInterfaceDialer
|
||||
domainStrategy dns.DomainStrategy
|
||||
fallbackDelay time.Duration
|
||||
networkStrategy C.NetworkStrategy
|
||||
networkType []C.InterfaceType
|
||||
fallbackNetworkType []C.InterfaceType
|
||||
networkFallbackDelay time.Duration
|
||||
overrideOption int
|
||||
overrideDestination M.Socksaddr
|
||||
// loopBack *loopBackDetector
|
||||
}
|
||||
|
||||
@@ -48,11 +52,15 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
return nil, err
|
||||
}
|
||||
outbound := &Outbound{
|
||||
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeDirect, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions),
|
||||
logger: logger,
|
||||
domainStrategy: dns.DomainStrategy(options.DomainStrategy),
|
||||
fallbackDelay: time.Duration(options.FallbackDelay),
|
||||
dialer: outboundDialer,
|
||||
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeDirect, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions),
|
||||
logger: logger,
|
||||
domainStrategy: dns.DomainStrategy(options.DomainStrategy),
|
||||
fallbackDelay: time.Duration(options.FallbackDelay),
|
||||
networkStrategy: C.NetworkStrategy(options.NetworkStrategy),
|
||||
networkType: common.Map(options.NetworkType, option.InterfaceType.Build),
|
||||
fallbackNetworkType: common.Map(options.FallbackNetworkType, option.InterfaceType.Build),
|
||||
networkFallbackDelay: time.Duration(options.NetworkFallbackDelay),
|
||||
dialer: outboundDialer,
|
||||
// loopBack: newLoopBackDetector(router),
|
||||
}
|
||||
//nolint:staticcheck
|
||||
@@ -170,10 +178,10 @@ func (h *Outbound) DialParallel(ctx context.Context, network string, destination
|
||||
return nil, E.New("no IPv6 address available for ", destination)
|
||||
}
|
||||
}
|
||||
return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, nil, nil, nil, h.fallbackDelay)
|
||||
return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, h.networkStrategy, h.networkType, h.fallbackNetworkType, h.fallbackDelay)
|
||||
}
|
||||
|
||||
func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy *C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||
func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||
ctx, metadata := adapter.ExtendContext(ctx)
|
||||
metadata.Outbound = h.Tag()
|
||||
metadata.Destination = destination
|
||||
@@ -213,7 +221,7 @@ func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, dest
|
||||
return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, networkStrategy, networkType, fallbackNetworkType, fallbackDelay)
|
||||
}
|
||||
|
||||
func (h *Outbound) ListenSerialNetworkPacket(ctx context.Context, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy *C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error) {
|
||||
func (h *Outbound) ListenSerialNetworkPacket(ctx context.Context, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error) {
|
||||
ctx, metadata := adapter.ExtendContext(ctx)
|
||||
metadata.Outbound = h.Tag()
|
||||
metadata.Destination = destination
|
||||
|
||||
@@ -42,21 +42,20 @@ func (d *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (d *Outbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
// Deprecated
|
||||
func (d *Outbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
defer conn.Close()
|
||||
for {
|
||||
conn.SetReadDeadline(time.Now().Add(C.DNSTimeout))
|
||||
err := HandleStreamDNSRequest(ctx, d.router, conn, metadata)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
if onClose != nil {
|
||||
onClose(err)
|
||||
}
|
||||
return
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Outbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
NewDNSPacketConnection(ctx, d.router, conn, nil, metadata)
|
||||
// Deprecated
|
||||
func (d *Outbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return NewDNSPacketConnection(ctx, d.router, conn, nil, metadata)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -22,22 +21,17 @@ func RegisterSelector(registry *outbound.Registry) {
|
||||
outbound.Register[option.SelectorOutboundOptions](registry, C.TypeSelector, NewSelector)
|
||||
}
|
||||
|
||||
var (
|
||||
_ adapter.OutboundGroup = (*Selector)(nil)
|
||||
_ adapter.ConnectionHandlerEx = (*Selector)(nil)
|
||||
_ adapter.PacketConnectionHandlerEx = (*Selector)(nil)
|
||||
)
|
||||
var _ adapter.OutboundGroup = (*Selector)(nil)
|
||||
|
||||
type Selector struct {
|
||||
outbound.Adapter
|
||||
ctx context.Context
|
||||
outbound adapter.OutboundManager
|
||||
connection adapter.ConnectionManager
|
||||
outboundManager adapter.OutboundManager
|
||||
logger logger.ContextLogger
|
||||
tags []string
|
||||
defaultTag string
|
||||
outbounds map[string]adapter.Outbound
|
||||
selected atomic.TypedValue[adapter.Outbound]
|
||||
selected adapter.Outbound
|
||||
interruptGroup *interrupt.Group
|
||||
interruptExternalConnections bool
|
||||
}
|
||||
@@ -46,8 +40,7 @@ func NewSelector(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
outbound := &Selector{
|
||||
Adapter: outbound.NewAdapter(C.TypeSelector, tag, nil, options.Outbounds),
|
||||
ctx: ctx,
|
||||
outbound: service.FromContext[adapter.OutboundManager](ctx),
|
||||
connection: service.FromContext[adapter.ConnectionManager](ctx),
|
||||
outboundManager: service.FromContext[adapter.OutboundManager](ctx),
|
||||
logger: logger,
|
||||
tags: options.Outbounds,
|
||||
defaultTag: options.Default,
|
||||
@@ -62,16 +55,15 @@ func NewSelector(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
}
|
||||
|
||||
func (s *Selector) Network() []string {
|
||||
selected := s.selected.Load()
|
||||
if selected == nil {
|
||||
if s.selected == nil {
|
||||
return []string{N.NetworkTCP, N.NetworkUDP}
|
||||
}
|
||||
return selected.Network()
|
||||
return s.selected.Network()
|
||||
}
|
||||
|
||||
func (s *Selector) Start() error {
|
||||
for i, tag := range s.tags {
|
||||
detour, loaded := s.outbound.Outbound(tag)
|
||||
detour, loaded := s.outboundManager.Outbound(tag)
|
||||
if !loaded {
|
||||
return E.New("outbound ", i, " not found: ", tag)
|
||||
}
|
||||
@@ -85,7 +77,7 @@ func (s *Selector) Start() error {
|
||||
if selected != "" {
|
||||
detour, loaded := s.outbounds[selected]
|
||||
if loaded {
|
||||
s.selected.Store(detour)
|
||||
s.selected = detour
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -97,16 +89,16 @@ func (s *Selector) Start() error {
|
||||
if !loaded {
|
||||
return E.New("default outbound not found: ", s.defaultTag)
|
||||
}
|
||||
s.selected.Store(detour)
|
||||
s.selected = detour
|
||||
return nil
|
||||
}
|
||||
|
||||
s.selected.Store(s.outbounds[s.tags[0]])
|
||||
s.selected = s.outbounds[s.tags[0]]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Selector) Now() string {
|
||||
selected := s.selected.Load()
|
||||
selected := s.selected
|
||||
if selected == nil {
|
||||
return s.tags[0]
|
||||
}
|
||||
@@ -122,9 +114,10 @@ func (s *Selector) SelectOutbound(tag string) bool {
|
||||
if !loaded {
|
||||
return false
|
||||
}
|
||||
if s.selected.Swap(detour) == detour {
|
||||
if s.selected == detour {
|
||||
return true
|
||||
}
|
||||
s.selected = detour
|
||||
if s.Tag() != "" {
|
||||
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
||||
if cacheFile != nil {
|
||||
@@ -139,7 +132,7 @@ func (s *Selector) SelectOutbound(tag string) bool {
|
||||
}
|
||||
|
||||
func (s *Selector) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
conn, err := s.selected.Load().DialContext(ctx, network, destination)
|
||||
conn, err := s.selected.DialContext(ctx, network, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -147,30 +140,32 @@ func (s *Selector) DialContext(ctx context.Context, network string, destination
|
||||
}
|
||||
|
||||
func (s *Selector) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
conn, err := s.selected.Load().ListenPacket(ctx, destination)
|
||||
conn, err := s.selected.ListenPacket(ctx, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.interruptGroup.NewPacketConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
|
||||
}
|
||||
|
||||
func (s *Selector) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
// TODO
|
||||
// Deprecated
|
||||
func (s *Selector) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||
selected := s.selected.Load()
|
||||
if outboundHandler, isHandler := selected.(adapter.ConnectionHandlerEx); isHandler {
|
||||
outboundHandler.NewConnectionEx(ctx, conn, metadata, onClose)
|
||||
if legacyHandler, ok := s.selected.(adapter.ConnectionHandler); ok {
|
||||
return legacyHandler.NewConnection(ctx, conn, metadata)
|
||||
} else {
|
||||
s.connection.NewConnection(ctx, selected, conn, metadata, onClose)
|
||||
return outbound.NewConnection(ctx, s.selected, conn, metadata)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Selector) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
// TODO
|
||||
// Deprecated
|
||||
func (s *Selector) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||
selected := s.selected.Load()
|
||||
if outboundHandler, isHandler := selected.(adapter.PacketConnectionHandlerEx); isHandler {
|
||||
outboundHandler.NewPacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
if legacyHandler, ok := s.selected.(adapter.PacketConnectionHandler); ok {
|
||||
return legacyHandler.NewPacketConnection(ctx, conn, metadata)
|
||||
} else {
|
||||
s.connection.NewPacketConnection(ctx, selected, conn, metadata, onClose)
|
||||
return outbound.NewPacketConnection(ctx, s.selected, conn, metadata)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,7 @@ type URLTest struct {
|
||||
outbound.Adapter
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
outbound adapter.OutboundManager
|
||||
connection adapter.ConnectionManager
|
||||
outboundManager adapter.OutboundManager
|
||||
logger log.ContextLogger
|
||||
tags []string
|
||||
link string
|
||||
@@ -53,8 +52,7 @@ func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
Adapter: outbound.NewAdapter(C.TypeURLTest, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.Outbounds),
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
outbound: service.FromContext[adapter.OutboundManager](ctx),
|
||||
connection: service.FromContext[adapter.ConnectionManager](ctx),
|
||||
outboundManager: service.FromContext[adapter.OutboundManager](ctx),
|
||||
logger: logger,
|
||||
tags: options.Outbounds,
|
||||
link: options.URL,
|
||||
@@ -72,13 +70,13 @@ func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
func (s *URLTest) Start() error {
|
||||
outbounds := make([]adapter.Outbound, 0, len(s.tags))
|
||||
for i, tag := range s.tags {
|
||||
detour, loaded := s.outbound.Outbound(tag)
|
||||
detour, loaded := s.outboundManager.Outbound(tag)
|
||||
if !loaded {
|
||||
return E.New("outbound ", i, " not found: ", tag)
|
||||
}
|
||||
outbounds = append(outbounds, detour)
|
||||
}
|
||||
group, err := NewURLTestGroup(s.ctx, s.outbound, s.logger, outbounds, s.link, s.interval, s.tolerance, s.idleTimeout, s.interruptExternalConnections)
|
||||
group, err := NewURLTestGroup(s.ctx, s.outboundManager, s.logger, outbounds, s.link, s.interval, s.tolerance, s.idleTimeout, s.interruptExternalConnections)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -162,14 +160,18 @@ func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (ne
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s *URLTest) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
// TODO
|
||||
// Deprecated
|
||||
func (s *URLTest) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||
s.connection.NewConnection(ctx, s, conn, metadata, onClose)
|
||||
return outbound.NewConnection(ctx, s, conn, metadata)
|
||||
}
|
||||
|
||||
func (s *URLTest) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
// TODO
|
||||
// Deprecated
|
||||
func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||
s.connection.NewPacketConnection(ctx, s, conn, metadata, onClose)
|
||||
return outbound.NewPacketConnection(ctx, s, conn, metadata)
|
||||
}
|
||||
|
||||
func (s *URLTest) InterfaceUpdated() {
|
||||
|
||||
@@ -82,16 +82,16 @@ func (h *Inbound) Close() error {
|
||||
}
|
||||
|
||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
var err error
|
||||
if h.tlsConfig != nil {
|
||||
tlsConn, err := tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||
if err != nil {
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source, ": TLS handshake"))
|
||||
return
|
||||
}
|
||||
conn = tlsConn
|
||||
}
|
||||
err := http.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
||||
err = http.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, 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))
|
||||
|
||||
@@ -60,40 +60,26 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
}
|
||||
}
|
||||
var masqueradeHandler http.Handler
|
||||
if options.Masquerade != nil && options.Masquerade.Type != "" {
|
||||
switch options.Masquerade.Type {
|
||||
case C.Hysterai2MasqueradeTypeFile:
|
||||
masqueradeHandler = http.FileServer(http.Dir(options.Masquerade.FileOptions.Directory))
|
||||
case C.Hysterai2MasqueradeTypeProxy:
|
||||
masqueradeURL, err := url.Parse(options.Masquerade.ProxyOptions.URL)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse masquerade URL")
|
||||
}
|
||||
if options.Masquerade != "" {
|
||||
masqueradeURL, err := url.Parse(options.Masquerade)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse masquerade URL")
|
||||
}
|
||||
switch masqueradeURL.Scheme {
|
||||
case "file":
|
||||
masqueradeHandler = http.FileServer(http.Dir(masqueradeURL.Path))
|
||||
case "http", "https":
|
||||
masqueradeHandler = &httputil.ReverseProxy{
|
||||
Rewrite: func(r *httputil.ProxyRequest) {
|
||||
r.SetURL(masqueradeURL)
|
||||
if !options.Masquerade.ProxyOptions.RewriteHost {
|
||||
r.Out.Host = r.In.Host
|
||||
}
|
||||
r.Out.Host = r.In.Host
|
||||
},
|
||||
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||
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:
|
||||
return nil, E.New("unknown masquerade type: ", options.Masquerade.Type)
|
||||
return nil, E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme)
|
||||
}
|
||||
}
|
||||
inbound := &Inbound{
|
||||
|
||||
@@ -85,9 +85,9 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
|
||||
}
|
||||
switch headerBytes[0] {
|
||||
case socks4.Version, socks5.Version:
|
||||
return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
||||
return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, metadata.Destination, onClose)
|
||||
default:
|
||||
return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
||||
return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,19 +110,11 @@ func (h *Inbound) streamUserPacketConnection(ctx context.Context, conn N.PacketC
|
||||
metadata.InboundType = h.Type()
|
||||
user, loaded := auth.UserFromContext[string](ctx)
|
||||
if !loaded {
|
||||
if !metadata.Destination.IsValid() {
|
||||
h.logger.InfoContext(ctx, "inbound packet connection")
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
}
|
||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
return
|
||||
}
|
||||
metadata.User = user
|
||||
if !metadata.Destination.IsValid() {
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection")
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
}
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
@@ -93,8 +93,6 @@ func (t *TProxy) Close() error {
|
||||
}
|
||||
|
||||
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()
|
||||
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||
t.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
||||
|
||||
@@ -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) {
|
||||
err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
||||
err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, metadata.Destination, onClose)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
if E.IsClosedOrCanceled(err) {
|
||||
@@ -92,19 +92,11 @@ func (h *Inbound) streamUserPacketConnection(ctx context.Context, conn N.PacketC
|
||||
metadata.InboundType = h.Type()
|
||||
user, loaded := auth.UserFromContext[string](ctx)
|
||||
if !loaded {
|
||||
if !metadata.Destination.IsValid() {
|
||||
h.logger.InfoContext(ctx, "inbound packet connection")
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
}
|
||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||
h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
return
|
||||
}
|
||||
metadata.User = user
|
||||
if !metadata.Destination.IsValid() {
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection")
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
}
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func (l *ProxyListener) acceptLoop() {
|
||||
}
|
||||
|
||||
func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error {
|
||||
return socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), l.authenticator, l, M.SocksaddrFromNet(conn.RemoteAddr()), nil)
|
||||
return socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), l.authenticator, nil, l, M.SocksaddrFromNet(conn.RemoteAddr()), M.Socksaddr{}, nil)
|
||||
}
|
||||
|
||||
func (l *ProxyListener) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
|
||||
@@ -159,16 +159,16 @@ func (h *Inbound) Close() error {
|
||||
}
|
||||
|
||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
var err error
|
||||
if h.tlsConfig != nil && h.transport == nil {
|
||||
tlsConn, err := tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||
if err != nil {
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source, ": TLS handshake"))
|
||||
return
|
||||
}
|
||||
conn = tlsConn
|
||||
}
|
||||
err := h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Source, onClose)
|
||||
err = h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Source, onClose)
|
||||
if err != nil {
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
|
||||
@@ -139,16 +139,16 @@ func (h *Inbound) Close() error {
|
||||
}
|
||||
|
||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
var err error
|
||||
if h.tlsConfig != nil && h.transport == nil {
|
||||
tlsConn, err := tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||
if err != nil {
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source, ": TLS handshake"))
|
||||
return
|
||||
}
|
||||
conn = tlsConn
|
||||
}
|
||||
err := h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Source, onClose)
|
||||
err = h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Source, onClose)
|
||||
if err != nil {
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
|
||||
@@ -153,16 +153,16 @@ func (h *Inbound) Close() error {
|
||||
}
|
||||
|
||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
var err error
|
||||
if h.tlsConfig != nil && h.transport == nil {
|
||||
tlsConn, err := tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||
if err != nil {
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source, ": TLS handshake"))
|
||||
return
|
||||
}
|
||||
conn = tlsConn
|
||||
}
|
||||
err := h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Source, onClose)
|
||||
err = h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Source, onClose)
|
||||
if err != nil {
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
func RegisterEndpoint(registry *endpoint.Registry) {
|
||||
@@ -55,21 +56,15 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if err != nil {
|
||||
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{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
System: options.System,
|
||||
Handler: ep,
|
||||
UDPTimeout: udpTimeout,
|
||||
UDPTimeout: time.Duration(options.UDPTimeout),
|
||||
Dialer: outboundDialer,
|
||||
CreateDialer: func(interfaceName string) N.Dialer {
|
||||
return common.Must1(dialer.NewDefault(ctx, option.DialerOptions{
|
||||
return common.Must1(dialer.NewDefault(service.FromContext[adapter.NetworkManager](ctx), option.DialerOptions{
|
||||
BindInterface: interfaceName,
|
||||
}))
|
||||
},
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
func RegisterOutbound(registry *outbound.Registry) {
|
||||
@@ -60,32 +61,13 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
peers := common.Map(options.Peers, func(it option.LegacyWireGuardPeer) wireguard.PeerOptions {
|
||||
return wireguard.PeerOptions{
|
||||
Endpoint: it.ServerOptions.Build(),
|
||||
PublicKey: it.PublicKey,
|
||||
PreSharedKey: it.PreSharedKey,
|
||||
AllowedIPs: it.AllowedIPs,
|
||||
// PersistentKeepaliveInterval: time.Duration(it.PersistentKeepaliveInterval),
|
||||
Reserved: it.Reserved,
|
||||
}
|
||||
})
|
||||
if len(peers) == 0 {
|
||||
peers = []wireguard.PeerOptions{{
|
||||
Endpoint: options.ServerOptions.Build(),
|
||||
PublicKey: options.PeerPublicKey,
|
||||
PreSharedKey: options.PreSharedKey,
|
||||
AllowedIPs: []netip.Prefix{netip.PrefixFrom(netip.IPv4Unspecified(), 0), netip.PrefixFrom(netip.IPv6Unspecified(), 0)},
|
||||
Reserved: options.Reserved,
|
||||
}}
|
||||
}
|
||||
wgEndpoint, err := wireguard.NewEndpoint(wireguard.EndpointOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
System: options.SystemInterface,
|
||||
Dialer: outboundDialer,
|
||||
CreateDialer: func(interfaceName string) N.Dialer {
|
||||
return common.Must1(dialer.NewDefault(ctx, option.DialerOptions{
|
||||
return common.Must1(dialer.NewDefault(service.FromContext[adapter.NetworkManager](ctx), option.DialerOptions{
|
||||
BindInterface: interfaceName,
|
||||
}))
|
||||
},
|
||||
@@ -100,7 +82,16 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
}
|
||||
return endpointAddresses[0], nil
|
||||
},
|
||||
Peers: peers,
|
||||
Peers: common.Map(options.Peers, func(it option.LegacyWireGuardPeer) wireguard.PeerOptions {
|
||||
return wireguard.PeerOptions{
|
||||
Endpoint: it.ServerOptions.Build(),
|
||||
PublicKey: it.PublicKey,
|
||||
PreSharedKey: it.PreSharedKey,
|
||||
AllowedIPs: it.AllowedIPs,
|
||||
// PersistentKeepaliveInterval: time.Duration(it.PersistentKeepaliveInterval),
|
||||
Reserved: it.Reserved,
|
||||
}
|
||||
}),
|
||||
Workers: options.Workers,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
290
route/conn.go
290
route/conn.go
@@ -5,49 +5,38 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"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/bufio"
|
||||
"github.com/sagernet/sing/common/canceler"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
|
||||
var _ adapter.ConnectionManager = (*ConnectionManager)(nil)
|
||||
|
||||
type ConnectionManager struct {
|
||||
logger logger.ContextLogger
|
||||
access sync.Mutex
|
||||
connections list.List[io.Closer]
|
||||
logger logger.ContextLogger
|
||||
monitor *ConnectionMonitor
|
||||
}
|
||||
|
||||
func NewConnectionManager(logger logger.ContextLogger) *ConnectionManager {
|
||||
return &ConnectionManager{
|
||||
logger: logger,
|
||||
logger: logger,
|
||||
monitor: NewConnectionMonitor(),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ConnectionManager) Start(stage adapter.StartStage) error {
|
||||
return nil
|
||||
func (m *ConnectionManager) Start() error {
|
||||
return m.monitor.Start()
|
||||
}
|
||||
|
||||
func (m *ConnectionManager) Close() error {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
for element := m.connections.Front(); element != nil; element = element.Next() {
|
||||
common.Close(element.Value)
|
||||
}
|
||||
m.connections.Init()
|
||||
return nil
|
||||
return m.monitor.Close()
|
||||
}
|
||||
|
||||
func (m *ConnectionManager) NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
@@ -56,38 +45,98 @@ func (m *ConnectionManager) NewConnection(ctx context.Context, this N.Dialer, co
|
||||
remoteConn net.Conn
|
||||
err error
|
||||
)
|
||||
if len(metadata.DestinationAddresses) > 0 || metadata.Destination.IsIP() {
|
||||
if len(metadata.DestinationAddresses) > 0 {
|
||||
remoteConn, err = dialer.DialSerialNetwork(ctx, this, N.NetworkTCP, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.NetworkType, metadata.FallbackNetworkType, metadata.FallbackDelay)
|
||||
} else {
|
||||
remoteConn, err = this.DialContext(ctx, N.NetworkTCP, metadata.Destination)
|
||||
}
|
||||
if err != nil {
|
||||
err = E.Cause(err, "open outbound connection")
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
m.logger.ErrorContext(ctx, err)
|
||||
m.logger.ErrorContext(ctx, "open outbound connection: ", err)
|
||||
return
|
||||
}
|
||||
err = N.ReportConnHandshakeSuccess(conn, remoteConn)
|
||||
if err != nil {
|
||||
err = E.Cause(err, "report handshake success")
|
||||
remoteConn.Close()
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
m.logger.ErrorContext(ctx, err)
|
||||
m.logger.ErrorContext(ctx, "report handshake success: ", err)
|
||||
return
|
||||
}
|
||||
m.access.Lock()
|
||||
element := m.connections.PushBack(conn)
|
||||
m.access.Unlock()
|
||||
onClose = N.AppendClose(onClose, func(it error) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.connections.Remove(element)
|
||||
})
|
||||
var done atomic.Bool
|
||||
if ctx.Done() != nil {
|
||||
onClose = N.AppendClose(onClose, m.monitor.Add(ctx, conn))
|
||||
}
|
||||
go m.connectionCopy(ctx, conn, remoteConn, false, &done, onClose)
|
||||
go m.connectionCopy(ctx, remoteConn, conn, true, &done, onClose)
|
||||
}
|
||||
|
||||
func (m *ConnectionManager) connectionCopy(ctx context.Context, source io.Reader, destination io.Writer, direction bool, done *atomic.Bool, onClose N.CloseHandlerFunc) {
|
||||
originSource := source
|
||||
var readCounters, writeCounters []N.CountFunc
|
||||
for {
|
||||
source, readCounters = N.UnwrapCountReader(source, readCounters)
|
||||
destination, writeCounters = N.UnwrapCountWriter(destination, writeCounters)
|
||||
if cachedSrc, isCached := source.(N.CachedReader); isCached {
|
||||
cachedBuffer := cachedSrc.ReadCached()
|
||||
if cachedBuffer != nil {
|
||||
if !cachedBuffer.IsEmpty() {
|
||||
dataLen := cachedBuffer.Len()
|
||||
for _, counter := range readCounters {
|
||||
counter(int64(dataLen))
|
||||
}
|
||||
_, err := destination.Write(cachedBuffer.Bytes())
|
||||
if err != nil {
|
||||
m.logger.ErrorContext(ctx, "connection upload payload: ", err)
|
||||
cachedBuffer.Release()
|
||||
if done.Swap(true) {
|
||||
if onClose != nil {
|
||||
onClose(err)
|
||||
}
|
||||
}
|
||||
common.Close(source, destination)
|
||||
return
|
||||
}
|
||||
for _, counter := range writeCounters {
|
||||
counter(int64(dataLen))
|
||||
}
|
||||
}
|
||||
cachedBuffer.Release()
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
_, err := bufio.CopyWithCounters(destination, source, originSource, readCounters, writeCounters)
|
||||
if _, dstDuplex := common.Cast[N.WriteCloser](destination); dstDuplex && err == nil {
|
||||
N.CloseWrite(destination)
|
||||
} else {
|
||||
common.Close(destination)
|
||||
}
|
||||
if done.Swap(true) {
|
||||
if onClose != nil {
|
||||
onClose(err)
|
||||
}
|
||||
common.Close(source, destination)
|
||||
}
|
||||
if !direction {
|
||||
if err == nil {
|
||||
m.logger.DebugContext(ctx, "connection upload finished")
|
||||
} else if !E.IsClosedOrCanceled(err) {
|
||||
m.logger.ErrorContext(ctx, "connection upload closed: ", err)
|
||||
} else {
|
||||
m.logger.TraceContext(ctx, "connection upload closed")
|
||||
}
|
||||
} else {
|
||||
if err == nil {
|
||||
m.logger.DebugContext(ctx, "connection download finished")
|
||||
} else if !E.IsClosedOrCanceled(err) {
|
||||
m.logger.ErrorContext(ctx, "connection download closed: ", err)
|
||||
} else {
|
||||
m.logger.TraceContext(ctx, "connection download closed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ConnectionManager) NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
var (
|
||||
@@ -97,7 +146,7 @@ func (m *ConnectionManager) NewPacketConnection(ctx context.Context, this N.Dial
|
||||
err error
|
||||
)
|
||||
if metadata.UDPConnect {
|
||||
if len(metadata.DestinationAddresses) > 0 || metadata.Destination.IsIP() {
|
||||
if len(metadata.DestinationAddresses) > 0 {
|
||||
if parallelDialer, isParallelDialer := this.(dialer.ParallelInterfaceDialer); isParallelDialer {
|
||||
remoteConn, err = dialer.DialSerialNetwork(ctx, parallelDialer, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.NetworkType, metadata.FallbackNetworkType, metadata.FallbackDelay)
|
||||
} else {
|
||||
@@ -153,107 +202,59 @@ func (m *ConnectionManager) NewPacketConnection(ctx context.Context, this N.Dial
|
||||
natConn.UpdateDestination(destinationAddress)
|
||||
}
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
if metadata.UDPTimeout > 0 {
|
||||
udpTimeout = metadata.UDPTimeout
|
||||
} else {
|
||||
protocol := metadata.Protocol
|
||||
if protocol == "" {
|
||||
protocol = C.PortProtocols[metadata.Destination.Port]
|
||||
}
|
||||
if protocol != "" {
|
||||
udpTimeout = C.ProtocolTimeouts[protocol]
|
||||
}
|
||||
}
|
||||
if udpTimeout > 0 {
|
||||
ctx, conn = canceler.NewPacketConn(ctx, conn, udpTimeout)
|
||||
}
|
||||
destination := bufio.NewPacketConn(remotePacketConn)
|
||||
m.access.Lock()
|
||||
element := m.connections.PushBack(conn)
|
||||
m.access.Unlock()
|
||||
onClose = N.AppendClose(onClose, func(it error) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.connections.Remove(element)
|
||||
})
|
||||
if ctx.Done() != nil {
|
||||
onClose = N.AppendClose(onClose, m.monitor.Add(ctx, conn))
|
||||
}
|
||||
var done atomic.Bool
|
||||
go m.packetConnectionCopy(ctx, conn, destination, false, &done, onClose)
|
||||
go m.packetConnectionCopy(ctx, destination, conn, true, &done, onClose)
|
||||
}
|
||||
|
||||
func (m *ConnectionManager) connectionCopy(ctx context.Context, source io.Reader, destination io.Writer, direction bool, done *atomic.Bool, onClose N.CloseHandlerFunc) {
|
||||
func (m *ConnectionManager) packetConnectionCopy(ctx context.Context, source N.PacketReader, destination N.PacketWriter, direction bool, done *atomic.Bool, onClose N.CloseHandlerFunc) {
|
||||
_, err := bufio.CopyPacket(destination, source)
|
||||
/*var readCounters, writeCounters []N.CountFunc
|
||||
var cachedPackets []*N.PacketBuffer
|
||||
originSource := source
|
||||
originDestination := destination
|
||||
var readCounters, writeCounters []N.CountFunc
|
||||
for {
|
||||
source, readCounters = N.UnwrapCountReader(source, readCounters)
|
||||
destination, writeCounters = N.UnwrapCountWriter(destination, writeCounters)
|
||||
if cachedSrc, isCached := source.(N.CachedReader); isCached {
|
||||
cachedBuffer := cachedSrc.ReadCached()
|
||||
if cachedBuffer != nil {
|
||||
dataLen := cachedBuffer.Len()
|
||||
_, err := destination.Write(cachedBuffer.Bytes())
|
||||
cachedBuffer.Release()
|
||||
if err != nil {
|
||||
if done.Swap(true) {
|
||||
onClose(err)
|
||||
}
|
||||
common.Close(originSource, originDestination)
|
||||
if !direction {
|
||||
m.logger.ErrorContext(ctx, "connection upload payload: ", err)
|
||||
} else {
|
||||
m.logger.ErrorContext(ctx, "connection download payload: ", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
for _, counter := range readCounters {
|
||||
counter(int64(dataLen))
|
||||
}
|
||||
for _, counter := range writeCounters {
|
||||
counter(int64(dataLen))
|
||||
}
|
||||
source, readCounters = N.UnwrapCountPacketReader(source, readCounters)
|
||||
destination, writeCounters = N.UnwrapCountPacketWriter(destination, writeCounters)
|
||||
if cachedReader, isCached := source.(N.CachedPacketReader); isCached {
|
||||
packet := cachedReader.ReadCachedPacket()
|
||||
if packet != nil {
|
||||
cachedPackets = append(cachedPackets, packet)
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
_, err := bufio.CopyWithCounters(destination, source, originSource, readCounters, writeCounters)
|
||||
if err != nil {
|
||||
common.Close(originDestination)
|
||||
} else if duplexDst, isDuplex := destination.(N.WriteCloser); isDuplex {
|
||||
err = duplexDst.CloseWrite()
|
||||
var handled bool
|
||||
if natConn, isNatConn := source.(udpnat.Conn); isNatConn {
|
||||
natConn.SetHandler(&udpHijacker{
|
||||
ctx: ctx,
|
||||
logger: m.logger,
|
||||
source: natConn,
|
||||
destination: destination,
|
||||
direction: direction,
|
||||
readCounters: readCounters,
|
||||
writeCounters: writeCounters,
|
||||
done: done,
|
||||
onClose: onClose,
|
||||
})
|
||||
handled = true
|
||||
}
|
||||
if cachedPackets != nil {
|
||||
_, err := bufio.WritePacketWithPool(originSource, destination, cachedPackets, readCounters, writeCounters)
|
||||
if err != nil {
|
||||
common.Close(originSource, originDestination)
|
||||
}
|
||||
} else {
|
||||
common.Close(originDestination)
|
||||
}
|
||||
if done.Swap(true) {
|
||||
onClose(err)
|
||||
common.Close(originSource, originDestination)
|
||||
}
|
||||
if !direction {
|
||||
if err == nil {
|
||||
m.logger.DebugContext(ctx, "connection upload finished")
|
||||
} else if !E.IsClosedOrCanceled(err) {
|
||||
m.logger.ErrorContext(ctx, "connection upload closed: ", err)
|
||||
} else {
|
||||
m.logger.TraceContext(ctx, "connection upload closed")
|
||||
}
|
||||
} else {
|
||||
if err == nil {
|
||||
m.logger.DebugContext(ctx, "connection download finished")
|
||||
} else if !E.IsClosedOrCanceled(err) {
|
||||
m.logger.ErrorContext(ctx, "connection download closed: ", err)
|
||||
} else {
|
||||
m.logger.TraceContext(ctx, "connection download closed")
|
||||
common.Close(source, destination)
|
||||
m.logger.ErrorContext(ctx, "packet upload payload: ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ConnectionManager) packetConnectionCopy(ctx context.Context, source N.PacketReader, destination N.PacketWriter, direction bool, done *atomic.Bool, onClose N.CloseHandlerFunc) {
|
||||
_, err := bufio.CopyPacket(destination, source)
|
||||
if handled {
|
||||
return
|
||||
}
|
||||
_, err := bufio.CopyPacketWithCounters(destination, source, originSource, readCounters, writeCounters)*/
|
||||
if !direction {
|
||||
if E.IsClosedOrCanceled(err) {
|
||||
m.logger.TraceContext(ctx, "packet upload closed")
|
||||
@@ -268,7 +269,58 @@ func (m *ConnectionManager) packetConnectionCopy(ctx context.Context, source N.P
|
||||
}
|
||||
}
|
||||
if !done.Swap(true) {
|
||||
onClose(err)
|
||||
if onClose != nil {
|
||||
onClose(err)
|
||||
}
|
||||
}
|
||||
common.Close(source, destination)
|
||||
}
|
||||
|
||||
/*type udpHijacker struct {
|
||||
ctx context.Context
|
||||
logger logger.ContextLogger
|
||||
source io.Closer
|
||||
destination N.PacketWriter
|
||||
direction bool
|
||||
readCounters []N.CountFunc
|
||||
writeCounters []N.CountFunc
|
||||
done *atomic.Bool
|
||||
onClose N.CloseHandlerFunc
|
||||
}
|
||||
|
||||
func (u *udpHijacker) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
|
||||
dataLen := buffer.Len()
|
||||
for _, counter := range u.readCounters {
|
||||
counter(int64(dataLen))
|
||||
}
|
||||
err := u.destination.WritePacket(buffer, source)
|
||||
if err != nil {
|
||||
common.Close(u.source, u.destination)
|
||||
u.logger.DebugContext(u.ctx, "packet upload closed: ", err)
|
||||
return
|
||||
}
|
||||
for _, counter := range u.writeCounters {
|
||||
counter(int64(dataLen))
|
||||
}
|
||||
}
|
||||
|
||||
func (u *udpHijacker) Close() error {
|
||||
var err error
|
||||
if !u.done.Swap(true) {
|
||||
err = common.Close(u.source, u.destination)
|
||||
if u.onClose != nil {
|
||||
u.onClose(net.ErrClosed)
|
||||
}
|
||||
}
|
||||
if u.direction {
|
||||
u.logger.TraceContext(u.ctx, "packet download closed")
|
||||
} else {
|
||||
u.logger.TraceContext(u.ctx, "packet upload closed")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *udpHijacker) Upstream() any {
|
||||
return u.destination
|
||||
}
|
||||
*/
|
||||
|
||||
124
route/conn_monitor.go
Normal file
124
route/conn_monitor.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
|
||||
type ConnectionMonitor struct {
|
||||
access sync.RWMutex
|
||||
reloadChan chan struct{}
|
||||
connections list.List[*monitorEntry]
|
||||
}
|
||||
|
||||
type monitorEntry struct {
|
||||
ctx context.Context
|
||||
closer io.Closer
|
||||
}
|
||||
|
||||
func NewConnectionMonitor() *ConnectionMonitor {
|
||||
return &ConnectionMonitor{
|
||||
reloadChan: make(chan struct{}, 1),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ConnectionMonitor) Add(ctx context.Context, closer io.Closer) N.CloseHandlerFunc {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
element := m.connections.PushBack(&monitorEntry{
|
||||
ctx: ctx,
|
||||
closer: closer,
|
||||
})
|
||||
select {
|
||||
case <-m.reloadChan:
|
||||
return nil
|
||||
default:
|
||||
select {
|
||||
case m.reloadChan <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
return func(it error) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.connections.Remove(element)
|
||||
select {
|
||||
case <-m.reloadChan:
|
||||
default:
|
||||
select {
|
||||
case m.reloadChan <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ConnectionMonitor) Start() error {
|
||||
go m.monitor()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ConnectionMonitor) Close() error {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
close(m.reloadChan)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ConnectionMonitor) monitor() {
|
||||
var (
|
||||
selectCases []reflect.SelectCase
|
||||
elements []*list.Element[*monitorEntry]
|
||||
)
|
||||
rootCase := reflect.SelectCase{
|
||||
Dir: reflect.SelectRecv,
|
||||
Chan: reflect.ValueOf(m.reloadChan),
|
||||
}
|
||||
for {
|
||||
m.access.RLock()
|
||||
if m.connections.Len() == 0 {
|
||||
m.access.RUnlock()
|
||||
if _, loaded := <-m.reloadChan; !loaded {
|
||||
return
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(elements) < m.connections.Len() {
|
||||
elements = make([]*list.Element[*monitorEntry], 0, m.connections.Len())
|
||||
}
|
||||
if len(selectCases) < m.connections.Len()+1 {
|
||||
selectCases = make([]reflect.SelectCase, 0, m.connections.Len()+1)
|
||||
}
|
||||
selectCases = selectCases[:1]
|
||||
selectCases[0] = rootCase
|
||||
for element := m.connections.Front(); element != nil; element = element.Next() {
|
||||
elements = append(elements, element)
|
||||
selectCases = append(selectCases, reflect.SelectCase{
|
||||
Dir: reflect.SelectRecv,
|
||||
Chan: reflect.ValueOf(element.Value.ctx.Done()),
|
||||
})
|
||||
}
|
||||
m.access.RUnlock()
|
||||
selected, _, loaded := reflect.Select(selectCases)
|
||||
if selected == 0 {
|
||||
if !loaded {
|
||||
return
|
||||
} else {
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
}
|
||||
element := elements[selected-1]
|
||||
m.access.Lock()
|
||||
m.connections.Remove(element)
|
||||
m.access.Unlock()
|
||||
element.Value.closer.Close() // maybe go close
|
||||
}
|
||||
}
|
||||
43
route/conn_monitor_test.go
Normal file
43
route/conn_monitor_test.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package route_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/route"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMonitor(t *testing.T) {
|
||||
t.Parallel()
|
||||
var closer myCloser
|
||||
closer.Add(1)
|
||||
monitor := route.NewConnectionMonitor()
|
||||
require.NoError(t, monitor.Start())
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
monitor.Add(ctx, &closer)
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
closer.Wait()
|
||||
close(done)
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(time.Second + 100*time.Millisecond):
|
||||
t.Fatal("timeout")
|
||||
}
|
||||
cancel()
|
||||
require.NoError(t, monitor.Close())
|
||||
}
|
||||
|
||||
type myCloser struct {
|
||||
sync.WaitGroup
|
||||
}
|
||||
|
||||
func (c *myCloser) Close() error {
|
||||
c.Done()
|
||||
return nil
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package route
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
@@ -10,7 +9,6 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
dnsOutbound "github.com/sagernet/sing-box/protocol/dns"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -56,7 +54,7 @@ func (r *Router) hijackDNSPacket(ctx context.Context, conn N.PacketConn, packetB
|
||||
|
||||
func ExchangeDNSPacket(ctx context.Context, router *Router, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext, destination M.Socksaddr) {
|
||||
err := exchangeDNSPacket(ctx, router, conn, buffer, metadata, destination)
|
||||
if err != nil && !errors.Is(err, tun.ErrDrop) && !E.IsClosedOrCanceled(err) {
|
||||
if err != nil && !E.IsClosedOrCanceled(err) {
|
||||
router.dnsLogger.ErrorContext(ctx, E.Cause(err, "process packet connection"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,8 +61,8 @@ func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOp
|
||||
autoDetectInterface: routeOptions.AutoDetectInterface,
|
||||
defaultOptions: adapter.NetworkOptions{
|
||||
BindInterface: routeOptions.DefaultInterface,
|
||||
RoutingMark: uint32(routeOptions.DefaultMark),
|
||||
NetworkStrategy: (*C.NetworkStrategy)(routeOptions.DefaultNetworkStrategy),
|
||||
RoutingMark: routeOptions.DefaultMark,
|
||||
NetworkStrategy: C.NetworkStrategy(routeOptions.DefaultNetworkStrategy),
|
||||
NetworkType: common.Map(routeOptions.DefaultNetworkType, option.InterfaceType.Build),
|
||||
FallbackNetworkType: common.Map(routeOptions.DefaultFallbackNetworkType, option.InterfaceType.Build),
|
||||
FallbackDelay: time.Duration(routeOptions.DefaultFallbackDelay),
|
||||
@@ -73,7 +73,7 @@ func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOp
|
||||
inbound: service.FromContext[adapter.InboundManager](ctx),
|
||||
outbound: service.FromContext[adapter.OutboundManager](ctx),
|
||||
}
|
||||
if routeOptions.DefaultNetworkStrategy != nil {
|
||||
if C.NetworkStrategy(routeOptions.DefaultNetworkStrategy) != C.NetworkStrategyDefault {
|
||||
if routeOptions.DefaultInterface != "" {
|
||||
return nil, E.New("`default_network_strategy` is conflict with `default_interface`")
|
||||
}
|
||||
@@ -241,7 +241,7 @@ func (r *NetworkManager) UpdateInterfaces() error {
|
||||
return it.Flags&net.FlagUp != 0
|
||||
})
|
||||
r.networkInterfaces.Store(newInterfaces)
|
||||
if len(newInterfaces) > 0 && !slices.EqualFunc(oldInterfaces, newInterfaces, func(oldInterface adapter.NetworkInterface, newInterface adapter.NetworkInterface) bool {
|
||||
if !slices.EqualFunc(oldInterfaces, newInterfaces, func(oldInterface adapter.NetworkInterface, newInterface adapter.NetworkInterface) bool {
|
||||
return oldInterface.Interface.Index == newInterface.Interface.Index &&
|
||||
oldInterface.Interface.Name == newInterface.Interface.Name &&
|
||||
oldInterface.Interface.Flags == newInterface.Interface.Flags &&
|
||||
|
||||
@@ -132,11 +132,23 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
if r.tracker != nil {
|
||||
conn = r.tracker.RoutedConnection(ctx, conn, metadata, selectedRule, selectedOutbound)
|
||||
}
|
||||
if outboundHandler, isHandler := selectedOutbound.(adapter.ConnectionHandlerEx); isHandler {
|
||||
outboundHandler.NewConnectionEx(ctx, conn, metadata, onClose)
|
||||
} else {
|
||||
r.connection.NewConnection(ctx, selectedOutbound, conn, metadata, onClose)
|
||||
legacyOutbound, isLegacy := selectedOutbound.(adapter.ConnectionHandler)
|
||||
if isLegacy {
|
||||
err = legacyOutbound.NewConnection(ctx, conn, metadata)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
if onClose != nil {
|
||||
onClose(err)
|
||||
}
|
||||
return E.Cause(err, F.ToString("outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]"))
|
||||
} else {
|
||||
if onClose != nil {
|
||||
onClose(nil)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
r.connection.NewConnection(ctx, selectedOutbound, conn, metadata, onClose)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -246,11 +258,16 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
if metadata.FakeIP {
|
||||
conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination)
|
||||
}
|
||||
if outboundHandler, isHandler := selectedOutbound.(adapter.PacketConnectionHandlerEx); isHandler {
|
||||
outboundHandler.NewPacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
} else {
|
||||
r.connection.NewPacketConnection(ctx, selectedOutbound, conn, metadata, onClose)
|
||||
legacyOutbound, isLegacy := selectedOutbound.(adapter.PacketConnectionHandler)
|
||||
if isLegacy {
|
||||
err = legacyOutbound.NewPacketConnection(ctx, conn, metadata)
|
||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||
if err != nil {
|
||||
return E.Cause(err, F.ToString("outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
r.connection.NewPacketConnection(ctx, selectedOutbound, conn, metadata, onClose)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -415,27 +432,14 @@ match:
|
||||
Fqdn: metadata.Destination.Fqdn,
|
||||
}
|
||||
}
|
||||
if routeOptions.NetworkStrategy != nil {
|
||||
metadata.NetworkStrategy = routeOptions.NetworkStrategy
|
||||
}
|
||||
if len(routeOptions.NetworkType) > 0 {
|
||||
metadata.NetworkType = routeOptions.NetworkType
|
||||
}
|
||||
if len(routeOptions.FallbackNetworkType) > 0 {
|
||||
metadata.FallbackNetworkType = routeOptions.FallbackNetworkType
|
||||
}
|
||||
if routeOptions.FallbackDelay != 0 {
|
||||
metadata.FallbackDelay = routeOptions.FallbackDelay
|
||||
}
|
||||
metadata.NetworkStrategy = routeOptions.NetworkStrategy
|
||||
metadata.FallbackDelay = routeOptions.FallbackDelay
|
||||
if routeOptions.UDPDisableDomainUnmapping {
|
||||
metadata.UDPDisableDomainUnmapping = true
|
||||
}
|
||||
if routeOptions.UDPConnect {
|
||||
metadata.UDPConnect = true
|
||||
}
|
||||
if routeOptions.UDPTimeout > 0 {
|
||||
metadata.UDPTimeout = routeOptions.UDPTimeout
|
||||
}
|
||||
}
|
||||
switch action := currentRule.Action().(type) {
|
||||
case *rule.RuleActionSniff:
|
||||
@@ -471,12 +475,8 @@ match:
|
||||
break match
|
||||
}
|
||||
}
|
||||
if !preMatch && inputPacketConn != nil && !metadata.Destination.IsFqdn() && !metadata.Destination.Addr.IsGlobalUnicast() {
|
||||
var timeout time.Duration
|
||||
if metadata.InboundType == C.TypeSOCKS {
|
||||
timeout = C.TCPTimeout
|
||||
}
|
||||
newBuffer, newPacketBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{Timeout: timeout}, inputConn, inputPacketConn)
|
||||
if !preMatch && metadata.Destination.Addr.IsUnspecified() {
|
||||
newBuffer, newPacketBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{}, inputConn, inputPacketConn)
|
||||
if newErr != nil {
|
||||
fatalErr = newErr
|
||||
return
|
||||
@@ -572,7 +572,8 @@ func (r *Router) actionSniff(
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if !metadata.Destination.Addr.IsGlobalUnicast() {
|
||||
// TODO: maybe always override destination
|
||||
if metadata.Destination.Addr.IsUnspecified() {
|
||||
metadata.Destination = destination
|
||||
}
|
||||
if len(packetBuffers) > 0 {
|
||||
|
||||
@@ -45,70 +45,69 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int,
|
||||
panic("no context")
|
||||
}
|
||||
var options dns.QueryOptions
|
||||
var (
|
||||
currentRuleIndex int
|
||||
currentRule adapter.DNSRule
|
||||
)
|
||||
if ruleIndex != -1 {
|
||||
currentRuleIndex = ruleIndex + 1
|
||||
}
|
||||
for currentRuleIndex, currentRule = range r.dnsRules[currentRuleIndex:] {
|
||||
if currentRule.WithAddressLimit() && !isAddressQuery {
|
||||
continue
|
||||
if ruleIndex < len(r.dnsRules) {
|
||||
dnsRules := r.dnsRules
|
||||
if ruleIndex != -1 {
|
||||
dnsRules = dnsRules[ruleIndex+1:]
|
||||
}
|
||||
metadata.ResetRuleCache()
|
||||
if currentRule.Match(metadata) {
|
||||
displayRuleIndex := currentRuleIndex
|
||||
if ruleIndex != -1 {
|
||||
displayRuleIndex += ruleIndex + 1
|
||||
for currentRuleIndex, currentRule := range dnsRules {
|
||||
if currentRule.WithAddressLimit() && !isAddressQuery {
|
||||
continue
|
||||
}
|
||||
ruleDescription := currentRule.String()
|
||||
if ruleDescription != "" {
|
||||
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] ", currentRule, " => ", currentRule.Action())
|
||||
} else {
|
||||
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
|
||||
}
|
||||
switch action := currentRule.Action().(type) {
|
||||
case *R.RuleActionDNSRoute:
|
||||
transport, loaded := r.transportMap[action.Server]
|
||||
if !loaded {
|
||||
r.dnsLogger.ErrorContext(ctx, "transport not found: ", action.Server)
|
||||
continue
|
||||
metadata.ResetRuleCache()
|
||||
if currentRule.Match(metadata) {
|
||||
displayRuleIndex := currentRuleIndex
|
||||
if displayRuleIndex != -1 {
|
||||
displayRuleIndex += displayRuleIndex + 1
|
||||
}
|
||||
_, isFakeIP := transport.(adapter.FakeIPTransport)
|
||||
if isFakeIP && !allowFakeIP {
|
||||
continue
|
||||
}
|
||||
if isFakeIP || action.DisableCache {
|
||||
options.DisableCache = true
|
||||
}
|
||||
if action.RewriteTTL != nil {
|
||||
options.RewriteTTL = action.RewriteTTL
|
||||
}
|
||||
if action.ClientSubnet.IsValid() {
|
||||
options.ClientSubnet = action.ClientSubnet
|
||||
}
|
||||
if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded {
|
||||
options.Strategy = domainStrategy
|
||||
ruleDescription := currentRule.String()
|
||||
if ruleDescription != "" {
|
||||
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] ", currentRule, " => ", currentRule.Action())
|
||||
} else {
|
||||
options.Strategy = r.defaultDomainStrategy
|
||||
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
|
||||
}
|
||||
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
|
||||
return transport, options, currentRule, currentRuleIndex
|
||||
case *R.RuleActionDNSRouteOptions:
|
||||
if action.DisableCache {
|
||||
options.DisableCache = true
|
||||
switch action := currentRule.Action().(type) {
|
||||
case *R.RuleActionDNSRoute:
|
||||
transport, loaded := r.transportMap[action.Server]
|
||||
if !loaded {
|
||||
r.dnsLogger.ErrorContext(ctx, "transport not found: ", action.Server)
|
||||
continue
|
||||
}
|
||||
_, isFakeIP := transport.(adapter.FakeIPTransport)
|
||||
if isFakeIP && !allowFakeIP {
|
||||
continue
|
||||
}
|
||||
if isFakeIP || action.DisableCache {
|
||||
options.DisableCache = true
|
||||
}
|
||||
if action.RewriteTTL != nil {
|
||||
options.RewriteTTL = action.RewriteTTL
|
||||
}
|
||||
if action.ClientSubnet.IsValid() {
|
||||
options.ClientSubnet = action.ClientSubnet
|
||||
}
|
||||
if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded {
|
||||
options.Strategy = domainStrategy
|
||||
} else {
|
||||
options.Strategy = r.defaultDomainStrategy
|
||||
}
|
||||
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
|
||||
return transport, options, currentRule, currentRuleIndex
|
||||
case *R.RuleActionDNSRouteOptions:
|
||||
if action.DisableCache {
|
||||
options.DisableCache = true
|
||||
}
|
||||
if action.RewriteTTL != nil {
|
||||
options.RewriteTTL = action.RewriteTTL
|
||||
}
|
||||
if action.ClientSubnet.IsValid() {
|
||||
options.ClientSubnet = action.ClientSubnet
|
||||
}
|
||||
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
|
||||
case *R.RuleActionReject:
|
||||
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
|
||||
return nil, options, currentRule, currentRuleIndex
|
||||
}
|
||||
if action.RewriteTTL != nil {
|
||||
options.RewriteTTL = action.RewriteTTL
|
||||
}
|
||||
if action.ClientSubnet.IsValid() {
|
||||
options.ClientSubnet = action.ClientSubnet
|
||||
}
|
||||
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
|
||||
case *R.RuleActionReject:
|
||||
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
|
||||
return nil, options, currentRule, currentRuleIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,19 +120,9 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int,
|
||||
}
|
||||
|
||||
func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
if len(message.Question) != 1 {
|
||||
r.dnsLogger.WarnContext(ctx, "bad question size: ", len(message.Question))
|
||||
responseMessage := mDNS.Msg{
|
||||
MsgHdr: mDNS.MsgHdr{
|
||||
Id: message.Id,
|
||||
Response: true,
|
||||
Rcode: mDNS.RcodeFormatError,
|
||||
},
|
||||
Question: message.Question,
|
||||
}
|
||||
return &responseMessage, nil
|
||||
if len(message.Question) > 0 {
|
||||
r.dnsLogger.DebugContext(ctx, "exchange ", formatQuestion(message.Question[0].String()))
|
||||
}
|
||||
r.dnsLogger.DebugContext(ctx, "exchange ", formatQuestion(message.Question[0].String()))
|
||||
var (
|
||||
response *mDNS.Msg
|
||||
cached bool
|
||||
@@ -145,14 +134,16 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
||||
var metadata *adapter.InboundContext
|
||||
ctx, metadata = adapter.ExtendContext(ctx)
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
metadata.QueryType = message.Question[0].Qtype
|
||||
switch metadata.QueryType {
|
||||
case mDNS.TypeA:
|
||||
metadata.IPVersion = 4
|
||||
case mDNS.TypeAAAA:
|
||||
metadata.IPVersion = 6
|
||||
if len(message.Question) > 0 {
|
||||
metadata.QueryType = message.Question[0].Qtype
|
||||
switch metadata.QueryType {
|
||||
case mDNS.TypeA:
|
||||
metadata.IPVersion = 4
|
||||
case mDNS.TypeAAAA:
|
||||
metadata.IPVersion = 6
|
||||
}
|
||||
metadata.Domain = fqdnToDomain(message.Question[0].Name)
|
||||
}
|
||||
metadata.Domain = fqdnToDomain(message.Question[0].Name)
|
||||
var (
|
||||
options dns.QueryOptions
|
||||
rule adapter.DNSRule
|
||||
@@ -211,7 +202,7 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.dnsReverseMapping != nil && response != nil && len(response.Answer) > 0 {
|
||||
if r.dnsReverseMapping != nil && len(message.Question) > 0 && response != nil && len(response.Answer) > 0 {
|
||||
if _, isFakeIP := transport.(adapter.FakeIPTransport); !isFakeIP {
|
||||
for _, answer := range response.Answer {
|
||||
switch record := answer.(type) {
|
||||
|
||||
@@ -262,7 +262,7 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
|
||||
Context: ctx,
|
||||
Name: "local",
|
||||
Address: "local",
|
||||
Dialer: common.Must1(dialer.NewDefault(ctx, option.DialerOptions{})),
|
||||
Dialer: common.Must1(dialer.NewDefault(router.network, option.DialerOptions{})),
|
||||
})))
|
||||
}
|
||||
defaultTransport = transports[0]
|
||||
|
||||
@@ -33,7 +33,7 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti
|
||||
RuleActionRouteOptions: RuleActionRouteOptions{
|
||||
OverrideAddress: M.ParseSocksaddrHostPort(action.RouteOptions.OverrideAddress, 0),
|
||||
OverridePort: action.RouteOptions.OverridePort,
|
||||
NetworkStrategy: (*C.NetworkStrategy)(action.RouteOptions.NetworkStrategy),
|
||||
NetworkStrategy: C.NetworkStrategy(action.RouteOptions.NetworkStrategy),
|
||||
FallbackDelay: time.Duration(action.RouteOptions.FallbackDelay),
|
||||
UDPDisableDomainUnmapping: action.RouteOptions.UDPDisableDomainUnmapping,
|
||||
UDPConnect: action.RouteOptions.UDPConnect,
|
||||
@@ -43,11 +43,10 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti
|
||||
return &RuleActionRouteOptions{
|
||||
OverrideAddress: M.ParseSocksaddrHostPort(action.RouteOptionsOptions.OverrideAddress, 0),
|
||||
OverridePort: action.RouteOptionsOptions.OverridePort,
|
||||
NetworkStrategy: (*C.NetworkStrategy)(action.RouteOptionsOptions.NetworkStrategy),
|
||||
NetworkStrategy: C.NetworkStrategy(action.RouteOptionsOptions.NetworkStrategy),
|
||||
FallbackDelay: time.Duration(action.RouteOptionsOptions.FallbackDelay),
|
||||
UDPDisableDomainUnmapping: action.RouteOptionsOptions.UDPDisableDomainUnmapping,
|
||||
UDPConnect: action.RouteOptionsOptions.UDPConnect,
|
||||
UDPTimeout: time.Duration(action.RouteOptionsOptions.UDPTimeout),
|
||||
}, nil
|
||||
case C.RuleActionTypeDirect:
|
||||
directDialer, err := dialer.New(ctx, option.DialerOptions(action.DirectOptions))
|
||||
@@ -147,13 +146,12 @@ func (r *RuleActionRoute) String() string {
|
||||
type RuleActionRouteOptions struct {
|
||||
OverrideAddress M.Socksaddr
|
||||
OverridePort uint16
|
||||
NetworkStrategy *C.NetworkStrategy
|
||||
NetworkStrategy C.NetworkStrategy
|
||||
NetworkType []C.InterfaceType
|
||||
FallbackNetworkType []C.InterfaceType
|
||||
FallbackDelay time.Duration
|
||||
UDPDisableDomainUnmapping bool
|
||||
UDPConnect bool
|
||||
UDPTimeout time.Duration
|
||||
}
|
||||
|
||||
func (r *RuleActionRouteOptions) Type() string {
|
||||
|
||||
37
test/go.mod
37
test/go.mod
@@ -12,9 +12,9 @@ require (
|
||||
github.com/docker/docker v27.3.1+incompatible
|
||||
github.com/docker/go-connections v0.5.0
|
||||
github.com/gofrs/uuid/v5 v5.3.0
|
||||
github.com/sagernet/quic-go v0.48.2-beta.1
|
||||
github.com/sagernet/sing v0.6.0-beta.5
|
||||
github.com/sagernet/sing-dns v0.4.0-beta.1
|
||||
github.com/sagernet/quic-go v0.48.1-beta.1
|
||||
github.com/sagernet/sing v0.6.0-alpha.18
|
||||
github.com/sagernet/sing-dns v0.4.0-alpha.3
|
||||
github.com/sagernet/sing-quic v0.4.0-alpha.4
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||
@@ -25,7 +25,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||
github.com/caddyserver/certmagic v0.20.0 // indirect
|
||||
@@ -33,7 +33,7 @@ require (
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/cretz/bine v0.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/distribution/reference v0.5.0 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
@@ -68,10 +68,10 @@ require (
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.9.7 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/errors v0.8.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
||||
@@ -79,27 +79,27 @@ require (
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 // indirect
|
||||
github.com/sagernet/cors v1.2.1 // indirect
|
||||
github.com/sagernet/fswatch v0.1.1 // indirect
|
||||
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff // indirect
|
||||
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
||||
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
|
||||
github.com/sagernet/sing-mux v0.3.0-alpha.1 // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 // indirect
|
||||
github.com/sagernet/sing-tun v0.6.0-beta.2 // indirect
|
||||
github.com/sagernet/sing-vmess v0.2.0-beta.1 // indirect
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.9 // indirect
|
||||
github.com/sagernet/sing-vmess v0.1.12 // indirect
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
|
||||
github.com/sagernet/utls v1.6.7 // indirect
|
||||
github.com/sagernet/wireguard-go v0.0.1-beta.5 // indirect
|
||||
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 // indirect
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
|
||||
go.opentelemetry.io/otel v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.31.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect
|
||||
go.opentelemetry.io/otel v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.32.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||
@@ -111,8 +111,7 @@ require (
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
golang.org/x/time v0.7.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-20241007155032-5fefd90f89a9 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
|
||||
google.golang.org/grpc v1.67.1 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
||||
92
test/go.sum
92
test/go.sum
@@ -1,7 +1,7 @@
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||
@@ -19,8 +19,8 @@ github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbe
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
|
||||
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
||||
github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
@@ -62,8 +62,8 @@ github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5X
|
||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0=
|
||||
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
||||
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
|
||||
@@ -78,6 +78,7 @@ github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6K
|
||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ=
|
||||
@@ -113,14 +114,14 @@ github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
|
||||
github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
|
||||
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
@@ -135,21 +136,21 @@ github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
|
||||
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
|
||||
github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs=
|
||||
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
||||
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff h1:mlohw3360Wg1BNGook/UHnISXhUx4Gd/3tVLs5T0nSs=
|
||||
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw=
|
||||
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3 h1:RxEz7LhPNiF/gX/Hg+OXr5lqsM9iVAgmaK1L1vzlDRM=
|
||||
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3/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/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
||||
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
||||
github.com/sagernet/quic-go v0.48.2-beta.1 h1:W0plrLWa1XtOWDTdX3CJwxmQuxkya12nN5BRGZ87kEg=
|
||||
github.com/sagernet/quic-go v0.48.2-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k=
|
||||
github.com/sagernet/quic-go v0.48.1-beta.1 h1:ElPaV5yzlXIKZpqFMAcUGax6vddi3zt4AEpT94Z0vwo=
|
||||
github.com/sagernet/quic-go v0.48.1-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/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||
github.com/sagernet/sing v0.6.0-beta.5 h1:RD2j8WmJsvAbbBkAlJWaiYmnd+v/JohBiweoew7kMwo=
|
||||
github.com/sagernet/sing v0.6.0-beta.5/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-dns v0.4.0-beta.1 h1:W1XkdhigwxDOMgMDVB+9kdomCpb7ExsZfB4acPcTZFY=
|
||||
github.com/sagernet/sing-dns v0.4.0-beta.1/go.mod h1:8wuFcoFkWM4vJuQyg8e97LyvDwe0/Vl7G839WLcKDs8=
|
||||
github.com/sagernet/sing v0.6.0-alpha.18 h1:ih4CurU8KvbhfagYjSqVrE2LR0oBSXSZTNH2sAGPGiM=
|
||||
github.com/sagernet/sing v0.6.0-alpha.18/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-alpha.3/go.mod h1:9LHcYKg2bGQpbtXrfNbopz8ok/zBK9ljiI2kmFG9JKg=
|
||||
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-quic v0.4.0-alpha.4 h1:P9xAx3nIfcqb9M8jfgs0uLm+VxCcaY++FCqaBfHY3dQ=
|
||||
@@ -160,23 +161,26 @@ 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-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-tun v0.6.0-beta.2 h1:GK7r2jWKm7RhlJGTq4QadgFcebQia1c3BO3OlYMcQJ0=
|
||||
github.com/sagernet/sing-tun v0.6.0-beta.2/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
||||
github.com/sagernet/sing-vmess v0.2.0-beta.1 h1:5sXQ23uwNlZuDvygzi0dFtnG0Csm/SNqTjAHXJkpuj4=
|
||||
github.com/sagernet/sing-vmess v0.2.0-beta.1/go.mod h1:fLyE1emIcvQ5DV8reFWnufquZ7MkCSYM5ThodsR9NrQ=
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.9 h1:Qf667035KnlydZ+ftj3U4HH+oddi3RdyKzBiCcnSgaI=
|
||||
github.com/sagernet/sing-tun v0.6.0-alpha.9/go.mod h1:TgvxE2YD7O9c/unHju0nWAGBGsVppWIuju13vlmdllM=
|
||||
github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg=
|
||||
github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I=
|
||||
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/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/wireguard-go v0.0.1-beta.5 h1:aBEsxJUMEONwOZqKPIkuAcv4zJV5p6XlzEN04CF0FXc=
|
||||
github.com/sagernet/wireguard-go v0.0.1-beta.5/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 h1:R0OMYAScomNAVpTfbHFpxqJpvwuhxSRi+g6z7gZhABs=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8/go.mod h1:K4J7/npM+VAMUeUmTa2JaA02JmyheP0GpRBOUvn3ecc=
|
||||
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/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spyzhov/ajson v0.9.4 h1:MVibcTCgO7DY4IlskdqIlCmDOsUOZ9P7oKj8ifdcf84=
|
||||
github.com/spyzhov/ajson v0.9.4/go.mod h1:a6oSw0MMb7Z5aD2tPoPO+jq11ETKgXUr2XktHdT8Wt8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
@@ -193,20 +197,20 @@ github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
|
||||
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
|
||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
|
||||
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
|
||||
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
|
||||
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
||||
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
||||
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
|
||||
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
|
||||
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
|
||||
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94=
|
||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 h1:cMyu9O88joYEaI47CnQkxO1XZdpoTF9fEnW2duIddhw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0/go.mod h1:6Am3rn7P9TVVeXYG+wtcGE7IE1tsQ+bP3AuWcKt/gOI=
|
||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@@ -242,8 +246,10 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -274,12 +280,10 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
||||
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
|
||||
@@ -253,7 +253,7 @@ func (t *Transport) recreateServers(iface *control.Interface, serverAddrs []neti
|
||||
return it.String()
|
||||
}), ","), "]")
|
||||
}
|
||||
serverDialer := common.Must1(dialer.NewDefault(t.options.Context, option.DialerOptions{
|
||||
serverDialer := common.Must1(dialer.NewDefault(t.networkManager, option.DialerOptions{
|
||||
BindInterface: iface.Name,
|
||||
UDPFragmentDefault: true,
|
||||
}))
|
||||
|
||||
@@ -71,7 +71,6 @@ func (w *systemDevice) Start() error {
|
||||
Inet6RouteAddress: common.Filter(w.options.AllowedAddress, func(it netip.Prefix) bool { return it.Addr().Is6() }),
|
||||
InterfaceMonitor: networkManager.InterfaceMonitor(),
|
||||
InterfaceFinder: networkManager.InterfaceFinder(),
|
||||
Logger: w.options.Logger,
|
||||
}
|
||||
// works with Linux, macOS with IFSCOPE routes, not tested on Windows
|
||||
if runtime.GOOS == "darwin" {
|
||||
|
||||
@@ -150,20 +150,14 @@ func (e *Endpoint) Start(resolve bool) error {
|
||||
connectAddr netip.AddrPort
|
||||
reserved [3]uint8
|
||||
)
|
||||
if len(e.peers) == 1 {
|
||||
peerLen := len(e.peers)
|
||||
if peerLen == 1 {
|
||||
isConnect = true
|
||||
connectAddr = e.peers[0].endpoint
|
||||
reserved = e.peers[0].reserved
|
||||
}
|
||||
bind = NewClientBind(e.options.Context, e.options.Logger, e.options.Dialer, isConnect, connectAddr, reserved)
|
||||
}
|
||||
if isWgListener || len(e.peers) > 1 {
|
||||
for _, peer := range e.peers {
|
||||
if peer.reserved != [3]uint8{} {
|
||||
bind.SetReservedForEndpoint(peer.endpoint, peer.reserved)
|
||||
}
|
||||
}
|
||||
}
|
||||
err := e.tunDevice.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user