mirror of
https://github.com/2dust/AndroidLibXrayLite.git
synced 2025-06-29 14:49:51 +00:00
Compare commits
32 commits
Author | SHA1 | Date | |
---|---|---|---|
|
8ad3e1ddf1 | ||
|
22f3e73a78 | ||
|
4de15a6c8d | ||
|
a031a553f6 | ||
|
ddcaecad0a | ||
|
c8b2ac1762 | ||
|
5cdcbc611f | ||
|
ae953fdc4d | ||
|
443ae5f577 | ||
|
bafb45ffcf | ||
|
52f9e06b67 | ||
|
fd5117ab47 | ||
|
c53ff63a3b | ||
|
6796ffc6ee | ||
|
08aa01a2c2 | ||
|
e01192ac83 | ||
|
a9226a4eb2 | ||
|
d18ff82b49 | ||
|
9e25db2025 | ||
|
6ea7a2636a | ||
|
2715a3b110 | ||
|
76b472eb98 | ||
|
74b37729bd | ||
|
6f650d49f3 | ||
|
acb16186b2 | ||
|
759e3e7232 | ||
|
d56745bc86 | ||
|
4ec3c7e177 | ||
|
d3d5265560 | ||
|
e1e7f75d07 | ||
|
c8a6ca7c5e | ||
|
d10cf1a43f |
11 changed files with 65600 additions and 18671 deletions
41
.github/workflows/main.yml
vendored
41
.github/workflows/main.yml
vendored
|
@ -18,10 +18,10 @@ jobs:
|
|||
steps:
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v5.4.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
|
||||
|
@ -30,24 +30,19 @@ jobs:
|
|||
go install golang.org/x/mobile/cmd/gomobile@latest
|
||||
export PATH=$PATH:~/go/bin
|
||||
|
||||
- name: Setup Android NDK
|
||||
uses: nttld/setup-ndk@v1
|
||||
id: setup-ndk
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v3.2.0
|
||||
with:
|
||||
ndk-version: r27
|
||||
add-to-path: true
|
||||
link-to-sdk: true
|
||||
local-cache: true
|
||||
log-accepted-android-sdk-licenses: false
|
||||
cmdline-tools-version: '12266719'
|
||||
packages: 'platforms;android-35 build-tools;35.0.0 platform-tools'
|
||||
|
||||
- name: Restore Android Symlinks
|
||||
- name: Install NDK
|
||||
run: |
|
||||
directory="${{ steps.setup-ndk.outputs.ndk-path }}/toolchains/llvm/prebuilt/linux-x86_64/bin"
|
||||
find "$directory" -type l | while read link; do
|
||||
current_target=$(readlink "$link")
|
||||
new_target="$directory/$(basename "$current_target")"
|
||||
ln -sf "$new_target" "$link"
|
||||
echo "Changed $(basename "$link") from $current_target to $new_target"
|
||||
done
|
||||
echo "y" | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager \
|
||||
--channel=3 \
|
||||
--install "ndk;29.0.13113456"
|
||||
echo "NDK_HOME=$ANDROID_HOME/ndk/29.0.13113456" >> $GITHUB_ENV
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
|
@ -57,8 +52,14 @@ jobs:
|
|||
gomobile init
|
||||
go mod tidy
|
||||
gomobile bind -v -androidapi 21 -trimpath -ldflags='-s -w -buildid=' ./
|
||||
env:
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
|
||||
- name: Upload build artifacts
|
||||
if: github.event.inputs.release_tag == ''
|
||||
uses: actions/upload-artifact@v4.6.2
|
||||
with:
|
||||
name: libv2ray
|
||||
path: |
|
||||
${{ github.workspace }}/libv2ray*r
|
||||
|
||||
- name: Upload AndroidLibXrayLite to release
|
||||
if: github.event.inputs.release_tag != ''
|
||||
|
@ -66,4 +67,4 @@ jobs:
|
|||
with:
|
||||
file: ./libv2ray*r
|
||||
tag: ${{ github.event.inputs.release_tag }}
|
||||
file_glob: true
|
||||
file_glob: true
|
17
.github/workflows/tidy.yml
vendored
17
.github/workflows/tidy.yml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout our repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v4.2.2
|
||||
with:
|
||||
fetch-depth: '0'
|
||||
|
||||
|
@ -36,8 +36,12 @@ jobs:
|
|||
id: compare-tags
|
||||
run: |
|
||||
if [ "$LATEST_TAG_NAME" != "$CURRENT_TAG_NAME" ]; then
|
||||
echo "Tags are different. Updating..."
|
||||
echo "needs_update=true" >> $GITHUB_ENV
|
||||
if [ "$(printf '%s\n' "$LATEST_TAG_NAME" "$CURRENT_TAG_NAME" | sort -V | tail -n1)" == "$CURRENT_TAG_NAME" ]; then
|
||||
echo "Upstream LATEST_TAG_NAME less than the CURRENT_TAG_NAME, no update needed."
|
||||
else
|
||||
echo "Tags are different. Updating..."
|
||||
echo "needs_update=true" >> $GITHUB_ENV
|
||||
fi
|
||||
else
|
||||
echo "Tags are the same. No update needed."
|
||||
echo "needs_update=false" >> $GITHUB_ENV
|
||||
|
@ -45,21 +49,24 @@ jobs:
|
|||
|
||||
- name: Setup Golang
|
||||
if: env.needs_update == 'true'
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v5.4.0
|
||||
with:
|
||||
go-version: 'stable'
|
||||
|
||||
- name: Update and commit changes
|
||||
if: env.needs_update == 'true'
|
||||
run: |
|
||||
go get -u
|
||||
go get github.com/xtls/xray-core@${{ env.LATEST_TAG_SHA }}
|
||||
go get gvisor.dev/gvisor@go
|
||||
go get github.com/OmarTariq612/goech@v0.0.0-20240405204721-8e2e1dafd3a0
|
||||
go mod tidy -v
|
||||
git diff
|
||||
|
||||
- name: Commit and push changes
|
||||
id: auto-commit-action
|
||||
if: env.needs_update == 'true'
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
uses: stefanzweifel/git-auto-commit-action@v5.1.0
|
||||
with:
|
||||
commit_message: Updating xray-core to ${{ env.LATEST_TAG_NAME }} ${{ env.LATEST_TAG_SHA }}
|
||||
tagging_message: ${{ env.LATEST_TAG_NAME }}
|
||||
|
|
39
.gitignore
vendored
39
.gitignore
vendored
|
@ -1,9 +1,44 @@
|
|||
.vscode
|
||||
# Ignore editor directories and files
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
# Ignore Gradle files
|
||||
gradle/build.gradle
|
||||
|
||||
# Ignore Go generated files
|
||||
*.pb.go
|
||||
binary*.go
|
||||
conf/demo/
|
||||
|
||||
# Ignore configuration and demo directories
|
||||
conf/demo/
|
||||
demo/
|
||||
|
||||
# Ignore asset files
|
||||
assets/
|
||||
|
||||
# Ignore V2Ray library files
|
||||
libv2ray*.[a|j]ar
|
||||
|
||||
# Ignore binary files
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Ignore backup and temporary files
|
||||
*~
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Ignore build and log directories
|
||||
build/
|
||||
logs/
|
||||
|
||||
# Ignore macOS files
|
||||
.DS_Store
|
||||
|
||||
# Ignore Node.js dependencies
|
||||
node_modules/
|
||||
|
||||
# Ignore Go vendor dependencies
|
||||
vendor/
|
||||
|
|
BIN
assets/geoip.dat
BIN
assets/geoip.dat
Binary file not shown.
82919
assets/geosite.dat
82919
assets/geosite.dat
File diff suppressed because one or more lines are too long
|
@ -7,75 +7,33 @@ set -o nounset
|
|||
# Set magic variables for current file & dir
|
||||
__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
__file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
|
||||
__base="$(basename ${__file} .sh)"
|
||||
__base="$(basename "${__file}" .sh)"
|
||||
|
||||
DATADIR="${__dir}/data"
|
||||
|
||||
|
||||
DATADIR=${__dir}/data
|
||||
|
||||
compile_dat () {
|
||||
local TMPDIR=$(mktemp -d)
|
||||
|
||||
trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; rm -rf $TMPDIR; trap ERR; exit 1' ERR
|
||||
|
||||
local GEOSITE=${GOPATH}/src/github.com/v2ray/domain-list-community
|
||||
if [[ -d ${GEOSITE} ]]; then
|
||||
cd ${GEOSITE} && git pull
|
||||
else
|
||||
mkdir -p ${GEOSITE}
|
||||
cd ${GEOSITE} && git clone https://github.com/v2ray/domain-list-community.git .
|
||||
fi
|
||||
go run main.go
|
||||
|
||||
if [[ -e dlc.dat ]]; then
|
||||
rm -f $DATADIR/geosite.dat
|
||||
mv dlc.dat $DATADIR/geosite.dat
|
||||
echo "----------> geosite.dat updated."
|
||||
else
|
||||
echo "----------> geosite.dat failed to update."
|
||||
fi
|
||||
|
||||
|
||||
if [[ ! -x $GOPATH/bin/geoip ]]; then
|
||||
go get -v -u github.com/v2ray/geoip
|
||||
fi
|
||||
|
||||
cd $TMPDIR
|
||||
curl -L -O http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country-CSV.zip
|
||||
unzip -q GeoLite2-Country-CSV.zip
|
||||
mkdir geoip && find . -name '*.csv' -exec mv -t ./geoip {} +
|
||||
$GOPATH/bin/geoip \
|
||||
--country=./geoip/GeoLite2-Country-Locations-en.csv \
|
||||
--ipv4=./geoip/GeoLite2-Country-Blocks-IPv4.csv \
|
||||
--ipv6=./geoip/GeoLite2-Country-Blocks-IPv6.csv
|
||||
|
||||
if [[ -e geoip.dat ]]; then
|
||||
rm -f $DATADIR/geoip.dat
|
||||
mv ./geoip.dat $DATADIR/geoip.dat
|
||||
echo "----------> geoip.dat updated."
|
||||
else
|
||||
echo "----------> geoip.dat failed to update."
|
||||
fi
|
||||
trap ERR
|
||||
return 0
|
||||
# Check for required dependencies
|
||||
check_dependencies() {
|
||||
command -v jq >/dev/null 2>&1 || { echo >&2 "jq is required but it's not installed. Aborting."; exit 1; }
|
||||
command -v go >/dev/null 2>&1 || { echo >&2 "Go is required but it's not installed. Aborting."; exit 1; }
|
||||
}
|
||||
|
||||
|
||||
download_dat () {
|
||||
wget -qO - https://api.github.com/repos/v2ray/geoip/releases/latest \
|
||||
| grep browser_download_url | cut -d '"' -f 4 \
|
||||
| wget -i - -O $DATADIR/geoip.dat
|
||||
# Download data function
|
||||
download_dat() {
|
||||
echo "Downloading geoip.dat..."
|
||||
curl -sL https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat -o "$DATADIR/geoip.dat"
|
||||
|
||||
wget -qO - https://api.github.com/repos/v2ray/domain-list-community/releases/latest \
|
||||
| grep browser_download_url | cut -d '"' -f 4 \
|
||||
| wget -i - -O $DATADIR/geosite.dat
|
||||
echo "Downloading geosite.dat..."
|
||||
curl -sL https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat -o "$DATADIR/geosite.dat"
|
||||
}
|
||||
|
||||
ACTION="${1:-}"
|
||||
if [[ -z $ACTION ]]; then
|
||||
ACTION=download
|
||||
fi
|
||||
# Main execution logic
|
||||
ACTION="${1:-download}"
|
||||
|
||||
check_dependencies
|
||||
|
||||
case $ACTION in
|
||||
"download") download_dat;;
|
||||
"compile") compile_dat;;
|
||||
"download") download_dat ;;
|
||||
*) echo "Invalid action: $ACTION" ; exit 1 ;;
|
||||
esac
|
||||
|
|
63
go.mod
63
go.mod
|
@ -1,55 +1,56 @@
|
|||
module github.com/2dust/AndroidLibXrayLite
|
||||
|
||||
go 1.24
|
||||
go 1.24.3
|
||||
|
||||
require (
|
||||
github.com/xtls/xray-core v1.8.25-0.20250306135015-2cba2c4d59e4
|
||||
golang.org/x/mobile v0.0.0-20250218173827-cd096645fcd3
|
||||
golang.org/x/sys v0.31.0
|
||||
github.com/xtls/xray-core v1.250516.1-0.20250608135303-fbae89d017ae
|
||||
golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/cloudflare/circl v1.6.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 // indirect
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/pprof v0.0.0-20250208200701-d0013a598941 // indirect
|
||||
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 // indirect
|
||||
github.com/juju/ratelimit v1.0.2 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.23.4 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pires/go-proxyproto v0.8.0 // indirect
|
||||
github.com/pires/go-proxyproto v0.8.1 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.50.0 // indirect
|
||||
github.com/refraction-networking/utls v1.6.7 // indirect
|
||||
github.com/quic-go/quic-go v0.52.0 // indirect
|
||||
github.com/refraction-networking/utls v1.7.3 // indirect
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/sagernet/sing v0.6.1 // indirect
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
|
||||
github.com/sagernet/sing v0.6.11 // indirect
|
||||
github.com/sagernet/sing-shadowsocks v0.2.8 // indirect
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // indirect
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
|
||||
github.com/vishvananda/netlink v1.3.0 // indirect
|
||||
github.com/vishvananda/netlink v1.3.1 // indirect
|
||||
github.com/vishvananda/netns v0.0.5 // indirect
|
||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
github.com/xtls/reality v0.0.0-20250608132114-50752aec6bfb // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/mock v0.5.2 // indirect
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect
|
||||
golang.org/x/mod v0.23.0 // indirect
|
||||
golang.org/x/net v0.37.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.10.0 // indirect
|
||||
golang.org/x/tools v0.30.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
|
||||
google.golang.org/grpc v1.71.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20250215002057-313350f3e697 // indirect
|
||||
lukechampine.com/blake3 v1.4.0 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20250606001031-fa4c4dd86b43 // indirect
|
||||
lukechampine.com/blake3 v1.4.1 // indirect
|
||||
)
|
||||
|
|
160
go.sum
160
go.sum
|
@ -2,8 +2,8 @@ github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 h1:Wo41lDOevRJS
|
|||
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
|
||||
github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
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=
|
||||
|
@ -26,40 +26,48 @@ 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.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/pprof v0.0.0-20250208200701-d0013a598941 h1:43XjGa6toxLpeksjcxs1jIoIyr+vUfOqY2c6HB4bpoc=
|
||||
github.com/google/pprof v0.0.0-20250208200701-d0013a598941/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18=
|
||||
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||
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/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||
github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
|
||||
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
|
||||
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
||||
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
|
||||
github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0=
|
||||
github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY=
|
||||
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
|
||||
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||
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/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.50.0 h1:3H/ld1pa3CYhkcc20TPIyG1bNsdhn9qZBGN3b9/UyUo=
|
||||
github.com/quic-go/quic-go v0.50.0/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
|
||||
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
||||
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
||||
github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA=
|
||||
github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
|
||||
github.com/refraction-networking/utls v1.7.3 h1:L0WRhHY7Oq1T0zkdzVZMR6zWZv+sXbHB9zcuvsAEqCo=
|
||||
github.com/refraction-networking/utls v1.7.3/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||
github.com/sagernet/sing v0.6.1 h1:mJ6e7Ir2wtCoGLbdnnXWBsNJu5YHtbXmv66inoE0zFA=
|
||||
github.com/sagernet/sing v0.6.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||
github.com/sagernet/sing v0.6.11 h1:BXYwLYw2srH1Kdp1vlZgrK+1lAan5i2UdZ4lPWqEgUU=
|
||||
github.com/sagernet/sing v0.6.11/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
@ -68,67 +76,67 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
|
||||
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
|
||||
github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
|
||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0=
|
||||
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM=
|
||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg=
|
||||
github.com/xtls/xray-core v1.8.25-0.20250306135015-2cba2c4d59e4 h1:oP0Jy1BN+wQSje74HgOdLm9wWihPM+exbCm12M+eTz8=
|
||||
github.com/xtls/xray-core v1.8.25-0.20250306135015-2cba2c4d59e4/go.mod h1:clXnUOnX6CKWBGgJY4ePYhb/EtTdSrUC7vPfT6m5p4c=
|
||||
github.com/xtls/reality v0.0.0-20250608132114-50752aec6bfb h1:X6ziJCMsFF8Ac/0F3W7+UbFdHZTu+r5nZ/smksHVxNQ=
|
||||
github.com/xtls/reality v0.0.0-20250608132114-50752aec6bfb/go.mod h1:yD47RN65bDLZgyHWMfFDiqlzrq4usDMt/Xzsk6tMbhw=
|
||||
github.com/xtls/xray-core v1.250516.1-0.20250608135303-fbae89d017ae h1:Be9MicJQI+Iup03zNG7QMEidpbJj3b4//IM8jIAnLKY=
|
||||
github.com/xtls/xray-core v1.250516.1-0.20250608135303-fbae89d017ae/go.mod h1:MkfIs2WZ5VLtZHAwDKosSS05Kx5zFFOzvly7Hy6pfPs=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
|
||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
|
||||
golang.org/x/mobile v0.0.0-20250218173827-cd096645fcd3 h1:0V/7Y1FEaFdAzb9DkVDh4QFp4vL4yYCiJ5cjk80lZyA=
|
||||
golang.org/x/mobile v0.0.0-20250218173827-cd096645fcd3/go.mod h1:j5VYNgQ6lZYZlzHFjdgS2UeqRSZunDk+/zXVTAIA3z4=
|
||||
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f h1:/n+PL2HlfqeSiDCuhdBbRNlGS/g2fM4OHufalHaTVG8=
|
||||
golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f/go.mod h1:ESkJ836Z6LpG6mTVAhA48LpfW/8fNR0ifStlH2axyfg=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
||||
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
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 v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
|
||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
@ -136,7 +144,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gvisor.dev/gvisor v0.0.0-20250215002057-313350f3e697 h1:3xb9C+AmVuRnDDAwJGJ8ZVAmcIourUD9DwHaAd5Ldyk=
|
||||
gvisor.dev/gvisor v0.0.0-20250215002057-313350f3e697/go.mod h1:5DMfjtclAbTIjbXqO1qCe2K5GKKxWz2JHvCChuTcJEM=
|
||||
lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w=
|
||||
lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0=
|
||||
gvisor.dev/gvisor v0.0.0-20250606001031-fa4c4dd86b43 h1:BEymU11L8DZSC4GNK48JYIR8EcHs+gFxtg9YfYlp68c=
|
||||
gvisor.dev/gvisor v0.0.0-20250606001031-fa4c4dd86b43/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g=
|
||||
lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=
|
||||
lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo=
|
||||
|
|
462
libv2ray_main.go
462
libv2ray_main.go
|
@ -14,187 +14,71 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
mobasset "golang.org/x/mobile/asset"
|
||||
|
||||
v2net "github.com/xtls/xray-core/common/net"
|
||||
v2filesystem "github.com/xtls/xray-core/common/platform/filesystem"
|
||||
coreapplog "github.com/xtls/xray-core/app/log"
|
||||
corecommlog "github.com/xtls/xray-core/common/log"
|
||||
corenet "github.com/xtls/xray-core/common/net"
|
||||
corefilesystem "github.com/xtls/xray-core/common/platform/filesystem"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
v2core "github.com/xtls/xray-core/core"
|
||||
v2stats "github.com/xtls/xray-core/features/stats"
|
||||
v2serial "github.com/xtls/xray-core/infra/conf/serial"
|
||||
core "github.com/xtls/xray-core/core"
|
||||
corestats "github.com/xtls/xray-core/features/stats"
|
||||
coreserial "github.com/xtls/xray-core/infra/conf/serial"
|
||||
_ "github.com/xtls/xray-core/main/distro/all"
|
||||
v2internet "github.com/xtls/xray-core/transport/internet"
|
||||
|
||||
v2applog "github.com/xtls/xray-core/app/log"
|
||||
v2commlog "github.com/xtls/xray-core/common/log"
|
||||
mobasset "golang.org/x/mobile/asset"
|
||||
)
|
||||
|
||||
// Constants for environment variables
|
||||
const (
|
||||
v2Asset = "xray.location.asset"
|
||||
coreAsset = "xray.location.asset"
|
||||
coreCert = "xray.location.cert"
|
||||
xudpBaseKey = "xray.xudp.basekey"
|
||||
)
|
||||
|
||||
/*
|
||||
V2RayPoint V2Ray Point Server
|
||||
This is territory of Go, so no getter and setters!
|
||||
*/
|
||||
type V2RayPoint struct {
|
||||
SupportSet V2RayVPNServiceSupportsSet
|
||||
statsManager v2stats.Manager
|
||||
|
||||
dialer *ProtectedDialer
|
||||
v2rayOP sync.Mutex
|
||||
closeChan chan struct{}
|
||||
|
||||
Vpoint *v2core.Instance
|
||||
IsRunning bool
|
||||
|
||||
DomainName string
|
||||
ConfigureFileContent string
|
||||
AsyncResolve bool
|
||||
// CoreController represents a controller for managing Xray core instance lifecycle
|
||||
type CoreController struct {
|
||||
CallbackHandler CoreCallbackHandler
|
||||
statsManager corestats.Manager
|
||||
coreMutex sync.Mutex
|
||||
coreInstance *core.Instance
|
||||
IsRunning bool
|
||||
}
|
||||
|
||||
/*V2RayVPNServiceSupportsSet To support Android VPN mode*/
|
||||
type V2RayVPNServiceSupportsSet interface {
|
||||
Setup(Conf string) int
|
||||
Prepare() int
|
||||
// CoreCallbackHandler defines interface for receiving callbacks and notifications from the core service
|
||||
type CoreCallbackHandler interface {
|
||||
Startup() int
|
||||
Shutdown() int
|
||||
Protect(int) bool
|
||||
OnEmitStatus(int, string) int
|
||||
}
|
||||
|
||||
/*RunLoop Run V2Ray main loop
|
||||
*/
|
||||
func (v *V2RayPoint) RunLoop(prefIPv6 bool) (err error) {
|
||||
v.v2rayOP.Lock()
|
||||
defer v.v2rayOP.Unlock()
|
||||
//Construct Context
|
||||
|
||||
if !v.IsRunning {
|
||||
v.closeChan = make(chan struct{})
|
||||
v.dialer.PrepareResolveChan()
|
||||
go func() {
|
||||
select {
|
||||
// wait until resolved
|
||||
case <-v.dialer.ResolveChan():
|
||||
// shutdown VPNService if server name can not reolved
|
||||
if !v.dialer.IsVServerReady() {
|
||||
log.Println("vServer cannot resolved, shutdown")
|
||||
v.StopLoop()
|
||||
v.SupportSet.Shutdown()
|
||||
}
|
||||
|
||||
// stop waiting if manually closed
|
||||
case <-v.closeChan:
|
||||
}
|
||||
}()
|
||||
|
||||
if v.AsyncResolve {
|
||||
go func() {
|
||||
v.dialer.PrepareDomain(v.DomainName, v.closeChan, prefIPv6)
|
||||
close(v.dialer.ResolveChan())
|
||||
}()
|
||||
} else {
|
||||
v.dialer.PrepareDomain(v.DomainName, v.closeChan, prefIPv6)
|
||||
close(v.dialer.ResolveChan())
|
||||
}
|
||||
|
||||
err = v.pointloop()
|
||||
}
|
||||
return
|
||||
// consoleLogWriter implements a log writer without datetime stamps
|
||||
// as Android system already adds timestamps to each log line
|
||||
type consoleLogWriter struct {
|
||||
logger *log.Logger // Standard logger
|
||||
}
|
||||
|
||||
/*StopLoop Stop V2Ray main loop
|
||||
*/
|
||||
func (v *V2RayPoint) StopLoop() (err error) {
|
||||
v.v2rayOP.Lock()
|
||||
defer v.v2rayOP.Unlock()
|
||||
if v.IsRunning {
|
||||
close(v.closeChan)
|
||||
v.shutdownInit()
|
||||
v.SupportSet.OnEmitStatus(0, "Closed")
|
||||
// setEnvVariable safely sets an environment variable and logs any errors encountered.
|
||||
func setEnvVariable(key, value string) {
|
||||
if err := os.Setenv(key, value); err != nil {
|
||||
log.Printf("Failed to set environment variable %s: %v. Please check your configuration.", key, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delegate Funcation
|
||||
func (v V2RayPoint) QueryStats(tag string, direct string) int64 {
|
||||
if v.statsManager == nil {
|
||||
return 0
|
||||
}
|
||||
counter := v.statsManager.GetCounter(fmt.Sprintf("outbound>>>%s>>>traffic>>>%s", tag, direct))
|
||||
if counter == nil {
|
||||
return 0
|
||||
}
|
||||
return counter.Set(0)
|
||||
}
|
||||
|
||||
func (v *V2RayPoint) shutdownInit() {
|
||||
v.IsRunning = false
|
||||
v.Vpoint.Close()
|
||||
v.Vpoint = nil
|
||||
v.statsManager = nil
|
||||
}
|
||||
|
||||
func (v *V2RayPoint) pointloop() error {
|
||||
log.Println("loading core config")
|
||||
config, err := v2serial.LoadJSONConfig(strings.NewReader(v.ConfigureFileContent))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("new core")
|
||||
v.Vpoint, err = v2core.New(config)
|
||||
if err != nil {
|
||||
v.Vpoint = nil
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
v.statsManager = v.Vpoint.GetFeature(v2stats.ManagerType()).(v2stats.Manager)
|
||||
|
||||
log.Println("start core")
|
||||
v.IsRunning = true
|
||||
if err := v.Vpoint.Start(); err != nil {
|
||||
v.IsRunning = false
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
v.SupportSet.Prepare()
|
||||
v.SupportSet.Setup("")
|
||||
v.SupportSet.OnEmitStatus(0, "Running")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *V2RayPoint) MeasureDelay(url string) (int64, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second)
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-v.closeChan:
|
||||
// cancel request if close called during meansure
|
||||
cancel()
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}()
|
||||
|
||||
return measureInstDelay(ctx, v.Vpoint, url)
|
||||
}
|
||||
|
||||
// InitV2Env set v2 asset path
|
||||
func InitV2Env(envPath string, key string) {
|
||||
//Initialize asset API, Since Raymond Will not let notify the asset location inside Process,
|
||||
//We need to set location outside V2Ray
|
||||
// InitCoreEnv initializes environment variables and file system handlers for the core
|
||||
// It sets up asset path, certificate path, XUDP base key and customizes the file reader
|
||||
// to support Android asset system
|
||||
func InitCoreEnv(envPath string, key string) {
|
||||
// Set asset/cert paths
|
||||
if len(envPath) > 0 {
|
||||
os.Setenv(v2Asset, envPath)
|
||||
}
|
||||
if len(key) > 0 {
|
||||
os.Setenv(xudpBaseKey, key)
|
||||
setEnvVariable(coreAsset, envPath)
|
||||
setEnvVariable(coreCert, envPath)
|
||||
}
|
||||
|
||||
//Now we handle read, fallback to gomobile asset (apk assets)
|
||||
v2filesystem.NewFileReader = func(path string) (io.ReadCloser, error) {
|
||||
// Set XUDP encryption key
|
||||
if len(key) > 0 {
|
||||
setEnvVariable(xudpBaseKey, key)
|
||||
}
|
||||
|
||||
// Custom file reader with path validation
|
||||
corefilesystem.NewFileReader = func(path string) (io.ReadCloser, error) {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
_, file := filepath.Split(path)
|
||||
return mobasset.Open(file)
|
||||
|
@ -203,107 +87,242 @@ func InitV2Env(envPath string, key string) {
|
|||
}
|
||||
}
|
||||
|
||||
func MeasureOutboundDelay(ConfigureFileContent string, url string) (int64, error) {
|
||||
config, err := v2serial.LoadJSONConfig(strings.NewReader(ConfigureFileContent))
|
||||
if err != nil {
|
||||
return -1, err
|
||||
// NewCoreController initializes and returns a new CoreController instance
|
||||
// Sets up the console log handler and associates it with the provided callback handler
|
||||
func NewCoreController(s CoreCallbackHandler) *CoreController {
|
||||
// Register custom logger
|
||||
if err := coreapplog.RegisterHandlerCreator(
|
||||
coreapplog.LogType_Console,
|
||||
func(lt coreapplog.LogType, options coreapplog.HandlerCreatorOptions) (corecommlog.Handler, error) {
|
||||
return corecommlog.NewLogger(createStdoutLogWriter()), nil
|
||||
},
|
||||
); err != nil {
|
||||
log.Printf("Failed to register log handler: %v", err)
|
||||
}
|
||||
|
||||
// dont listen to anything for test purpose
|
||||
return &CoreController{
|
||||
CallbackHandler: s,
|
||||
}
|
||||
}
|
||||
|
||||
// StartLoop initializes and starts the core processing loop
|
||||
// Thread-safe method that configures and runs the Xray core with the provided configuration
|
||||
// Returns immediately if the core is already running
|
||||
func (x *CoreController) StartLoop(configContent string) (err error) {
|
||||
x.coreMutex.Lock()
|
||||
defer x.coreMutex.Unlock()
|
||||
|
||||
if x.IsRunning {
|
||||
log.Println("Core is already running")
|
||||
return nil
|
||||
}
|
||||
|
||||
return x.doStartLoop(configContent)
|
||||
}
|
||||
|
||||
// StopLoop safely stops the core processing loop and releases resources
|
||||
// Thread-safe method that shuts down the core instance and triggers necessary callbacks
|
||||
func (x *CoreController) StopLoop() error {
|
||||
x.coreMutex.Lock()
|
||||
defer x.coreMutex.Unlock()
|
||||
|
||||
if x.IsRunning {
|
||||
x.doShutdown()
|
||||
x.CallbackHandler.OnEmitStatus(0, "Core stopped")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// QueryStats retrieves and resets traffic statistics for a specific outbound tag and direction
|
||||
// Returns the accumulated traffic value and resets the counter to zero
|
||||
// Returns 0 if the stats manager is not initialized or the counter doesn't exist
|
||||
func (x *CoreController) QueryStats(tag string, direct string) int64 {
|
||||
if x.statsManager == nil {
|
||||
return 0
|
||||
}
|
||||
counter := x.statsManager.GetCounter(fmt.Sprintf("outbound>>>%s>>>traffic>>>%s", tag, direct))
|
||||
if counter == nil {
|
||||
return 0
|
||||
}
|
||||
return counter.Set(0)
|
||||
}
|
||||
|
||||
// MeasureDelay measures network latency to a specified URL through the current core instance
|
||||
// Uses a 12-second timeout context and returns the round-trip time in milliseconds
|
||||
// An error is returned if the connection fails or returns an unexpected status
|
||||
func (x *CoreController) MeasureDelay(url string) (int64, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second)
|
||||
defer cancel()
|
||||
|
||||
return measureInstDelay(ctx, x.coreInstance, url)
|
||||
}
|
||||
|
||||
// MeasureOutboundDelay measures the outbound delay for a given configuration and URL
|
||||
func MeasureOutboundDelay(ConfigureFileContent string, url string) (int64, error) {
|
||||
config, err := coreserial.LoadJSONConfig(strings.NewReader(ConfigureFileContent))
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("config load error: %w", err)
|
||||
}
|
||||
|
||||
// Simplify config for testing
|
||||
config.Inbound = nil
|
||||
// config.App: (fakedns), log, dispatcher, InboundConfig, OutboundConfig, (stats), router, dns, (policy)
|
||||
// keep only basic features
|
||||
var essentialApp []*serial.TypedMessage
|
||||
for _, app := range config.App {
|
||||
if app.Type == "xray.app.proxyman.OutboundConfig" || app.Type == "xray.app.dispatcher.Config" || app.Type == "xray.app.log.Config" {
|
||||
if app.Type == "xray.app.proxyman.OutboundConfig" ||
|
||||
app.Type == "xray.app.dispatcher.Config" ||
|
||||
app.Type == "xray.app.log.Config" {
|
||||
essentialApp = append(essentialApp, app)
|
||||
}
|
||||
}
|
||||
config.App = essentialApp
|
||||
|
||||
inst, err := v2core.New(config)
|
||||
inst, err := core.New(config)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
return -1, fmt.Errorf("instance creation failed: %w", err)
|
||||
}
|
||||
|
||||
inst.Start()
|
||||
delay, err := measureInstDelay(context.Background(), inst, url)
|
||||
inst.Close()
|
||||
return delay, err
|
||||
}
|
||||
|
||||
/*NewV2RayPoint new V2RayPoint*/
|
||||
func NewV2RayPoint(s V2RayVPNServiceSupportsSet, adns bool) *V2RayPoint {
|
||||
// inject our own log writer
|
||||
v2applog.RegisterHandlerCreator(v2applog.LogType_Console,
|
||||
func(lt v2applog.LogType,
|
||||
options v2applog.HandlerCreatorOptions) (v2commlog.Handler, error) {
|
||||
return v2commlog.NewLogger(createStdoutLogWriter()), nil
|
||||
})
|
||||
|
||||
dialer := NewPreotectedDialer(s)
|
||||
v2internet.UseAlternativeSystemDialer(dialer)
|
||||
return &V2RayPoint{
|
||||
SupportSet: s,
|
||||
dialer: dialer,
|
||||
AsyncResolve: adns,
|
||||
if err := inst.Start(); err != nil {
|
||||
return -1, fmt.Errorf("startup failed: %w", err)
|
||||
}
|
||||
defer inst.Close()
|
||||
return measureInstDelay(context.Background(), inst, url)
|
||||
}
|
||||
|
||||
/*
|
||||
CheckVersionX string
|
||||
This func will return libv2ray binding version and V2Ray version used.
|
||||
*/
|
||||
// CheckVersionX returns the library and Xray versions
|
||||
func CheckVersionX() string {
|
||||
var version = 30
|
||||
return fmt.Sprintf("Lib v%d, Xray-core v%s", version, v2core.Version())
|
||||
var version = 33
|
||||
return fmt.Sprintf("Lib v%d, Xray-core v%s", version, core.Version())
|
||||
}
|
||||
|
||||
func measureInstDelay(ctx context.Context, inst *v2core.Instance, url string) (int64, error) {
|
||||
// doShutdown shuts down the Xray instance and cleans up resources
|
||||
func (x *CoreController) doShutdown() {
|
||||
if x.coreInstance != nil {
|
||||
if err := x.coreInstance.Close(); err != nil {
|
||||
log.Printf("core shutdown error: %v", err)
|
||||
}
|
||||
x.coreInstance = nil
|
||||
}
|
||||
x.IsRunning = false
|
||||
x.statsManager = nil
|
||||
}
|
||||
|
||||
// doStartLoop sets up and starts the Xray core
|
||||
func (x *CoreController) doStartLoop(configContent string) error {
|
||||
log.Println("initializing core...")
|
||||
config, err := coreserial.LoadJSONConfig(strings.NewReader(configContent))
|
||||
if err != nil {
|
||||
return fmt.Errorf("config error: %w", err)
|
||||
}
|
||||
|
||||
x.coreInstance, err = core.New(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("core init failed: %w", err)
|
||||
}
|
||||
x.statsManager = x.coreInstance.GetFeature(corestats.ManagerType()).(corestats.Manager)
|
||||
|
||||
log.Println("starting core...")
|
||||
x.IsRunning = true
|
||||
if err := x.coreInstance.Start(); err != nil {
|
||||
x.IsRunning = false
|
||||
return fmt.Errorf("startup failed: %w", err)
|
||||
}
|
||||
|
||||
x.CallbackHandler.Startup()
|
||||
x.CallbackHandler.OnEmitStatus(0, "Started successfully, running")
|
||||
|
||||
log.Println("Starting core successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// measureInstDelay measures the delay for an instance to a given URL
|
||||
func measureInstDelay(ctx context.Context, inst *core.Instance, url string) (int64, error) {
|
||||
if inst == nil {
|
||||
return -1, errors.New("core instance nil")
|
||||
return -1, errors.New("core instance is nil")
|
||||
}
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSHandshakeTimeout: 6 * time.Second,
|
||||
DisableKeepAlives: true,
|
||||
DisableKeepAlives: false,
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
dest, err := v2net.ParseDestination(fmt.Sprintf("%s:%s", network, addr))
|
||||
dest, err := corenet.ParseDestination(fmt.Sprintf("%s:%s", network, addr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v2core.Dial(ctx, inst, dest)
|
||||
return core.Dial(ctx, inst, dest)
|
||||
},
|
||||
}
|
||||
|
||||
c := &http.Client{
|
||||
client := &http.Client{
|
||||
Transport: tr,
|
||||
Timeout: 12 * time.Second,
|
||||
}
|
||||
|
||||
if len(url) <= 0 {
|
||||
if url == "" {
|
||||
url = "https://www.google.com/generate_204"
|
||||
}
|
||||
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
start := time.Now()
|
||||
resp, err := c.Do(req)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
return -1, fmt.Errorf("failed to create HTTP request: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
|
||||
return -1, fmt.Errorf("status != 20x: %s", resp.Status)
|
||||
var minDuration int64 = -1
|
||||
success := false
|
||||
var lastErr error
|
||||
|
||||
// Add exception handling and increase retry attempts
|
||||
const attempts = 2
|
||||
for i := 0; i < attempts; i++ {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Return immediately when context is canceled
|
||||
if !success {
|
||||
return -1, ctx.Err()
|
||||
}
|
||||
return minDuration, nil
|
||||
default:
|
||||
// Continue execution
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
|
||||
// Ensure response body is closed
|
||||
defer func(resp *http.Response) {
|
||||
if resp != nil && resp.Body != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}(resp)
|
||||
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
|
||||
lastErr = fmt.Errorf("invalid status: %s", resp.Status)
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle possible errors when reading response body
|
||||
if _, err := io.Copy(io.Discard, resp.Body); err != nil {
|
||||
lastErr = fmt.Errorf("failed to read response body: %w", err)
|
||||
continue
|
||||
}
|
||||
|
||||
duration := time.Since(start).Milliseconds()
|
||||
if !success || duration < minDuration {
|
||||
minDuration = duration
|
||||
}
|
||||
|
||||
success = true
|
||||
}
|
||||
resp.Body.Close()
|
||||
return time.Since(start).Milliseconds(), nil
|
||||
}
|
||||
|
||||
// This struct creates our own log writer without datatime stamp
|
||||
// As Android adds time stamps on each line
|
||||
type consoleLogWriter struct {
|
||||
logger *log.Logger
|
||||
if !success {
|
||||
return -1, lastErr
|
||||
}
|
||||
return minDuration, nil
|
||||
}
|
||||
|
||||
// Log writer implementation
|
||||
func (w *consoleLogWriter) Write(s string) error {
|
||||
w.logger.Print(s)
|
||||
return nil
|
||||
|
@ -313,10 +332,11 @@ func (w *consoleLogWriter) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// This logger won't print data/time stamps
|
||||
func createStdoutLogWriter() v2commlog.WriterCreator {
|
||||
return func() v2commlog.Writer {
|
||||
// createStdoutLogWriter creates a logger that won't print date/time stamps
|
||||
func createStdoutLogWriter() corecommlog.WriterCreator {
|
||||
return func() corecommlog.Writer {
|
||||
return &consoleLogWriter{
|
||||
logger: log.New(os.Stdout, "", 0)}
|
||||
logger: log.New(os.Stdout, "", 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,339 +0,0 @@
|
|||
package libv2ray
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
v2net "github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/features/dns"
|
||||
"github.com/xtls/xray-core/features/outbound"
|
||||
v2internet "github.com/xtls/xray-core/transport/internet"
|
||||
)
|
||||
|
||||
type protectSet interface {
|
||||
Protect(int) bool
|
||||
}
|
||||
|
||||
type resolved struct {
|
||||
domain string
|
||||
IPs []net.IP
|
||||
Port int
|
||||
lastResolved time.Time
|
||||
ipIdx uint8
|
||||
ipLock sync.Mutex
|
||||
lastSwitched time.Time
|
||||
}
|
||||
|
||||
// NextIP switch to another resolved result.
|
||||
// there still be race-condition here if multiple err concurently occured
|
||||
// may cause idx keep switching,
|
||||
// but that's an outside error can hardly handled here
|
||||
func (r *resolved) NextIP() {
|
||||
r.ipLock.Lock()
|
||||
defer r.ipLock.Unlock()
|
||||
|
||||
if len(r.IPs) > 1 {
|
||||
|
||||
// throttle, don't switch too quickly
|
||||
now := time.Now()
|
||||
if now.Sub(r.lastSwitched) < time.Second*5 {
|
||||
log.Println("switch too quickly")
|
||||
return
|
||||
}
|
||||
r.lastSwitched = now
|
||||
r.ipIdx++
|
||||
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
if r.ipIdx >= uint8(len(r.IPs)) {
|
||||
r.ipIdx = 0
|
||||
}
|
||||
|
||||
log.Printf("switched to next IP: %v", r.IPs[r.ipIdx])
|
||||
}
|
||||
|
||||
func (r *resolved) currentIP() net.IP {
|
||||
r.ipLock.Lock()
|
||||
defer r.ipLock.Unlock()
|
||||
if len(r.IPs) > 0 {
|
||||
return r.IPs[r.ipIdx]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewPreotectedDialer ...
|
||||
func NewPreotectedDialer(p protectSet) *ProtectedDialer {
|
||||
d := &ProtectedDialer{
|
||||
// prefer native lookup on Android
|
||||
resolver: &net.Resolver{PreferGo: false},
|
||||
protectSet: p,
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// ProtectedDialer ...
|
||||
type ProtectedDialer struct {
|
||||
currentServer string
|
||||
resolveChan chan struct{}
|
||||
preferIPv6 bool
|
||||
|
||||
vServer *resolved
|
||||
resolver *net.Resolver
|
||||
|
||||
protectSet
|
||||
}
|
||||
|
||||
func (d *ProtectedDialer) IsVServerReady() bool {
|
||||
return (d.vServer != nil)
|
||||
}
|
||||
|
||||
func (d *ProtectedDialer) PrepareResolveChan() {
|
||||
d.resolveChan = make(chan struct{})
|
||||
}
|
||||
|
||||
func (d *ProtectedDialer) ResolveChan() chan struct{} {
|
||||
return d.resolveChan
|
||||
}
|
||||
|
||||
// simplicated version of golang: internetAddrList in src/net/ipsock.go
|
||||
func (d *ProtectedDialer) lookupAddr(addr string) (*resolved, error) {
|
||||
|
||||
var (
|
||||
err error
|
||||
host, port string
|
||||
portnum int
|
||||
)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if host, port, err = net.SplitHostPort(addr); err != nil {
|
||||
log.Printf("PrepareDomain SplitHostPort Err: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if portnum, err = d.resolver.LookupPort(ctx, "tcp", port); err != nil {
|
||||
log.Printf("PrepareDomain LookupPort Err: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addrs, err := d.resolver.LookupIPAddr(ctx, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(addrs) == 0 {
|
||||
return nil, fmt.Errorf("domain %s Failed to resolve", addr)
|
||||
}
|
||||
|
||||
IPs := make([]net.IP, 0)
|
||||
//ipv6 is prefer, append ipv6 then ipv4
|
||||
//ipv6 is not prefer, append ipv4 then ipv6
|
||||
if(d.preferIPv6) {
|
||||
for _, ia := range addrs {
|
||||
if(ia.IP.To4() == nil) {
|
||||
IPs = append(IPs, ia.IP)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, ia := range addrs {
|
||||
if(ia.IP.To4() != nil) {
|
||||
IPs = append(IPs, ia.IP)
|
||||
}
|
||||
}
|
||||
if(!d.preferIPv6) {
|
||||
for _, ia := range addrs {
|
||||
if(ia.IP.To4() == nil) {
|
||||
IPs = append(IPs, ia.IP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rs := &resolved{
|
||||
domain: host,
|
||||
IPs: IPs,
|
||||
Port: portnum,
|
||||
lastResolved: time.Now(),
|
||||
}
|
||||
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
// PrepareDomain caches direct v2ray server host
|
||||
func (d *ProtectedDialer) PrepareDomain(domainName string, closeCh <-chan struct{}, prefIPv6 bool) {
|
||||
log.Printf("Preparing Domain: %s", domainName)
|
||||
d.currentServer = domainName
|
||||
d.preferIPv6 = prefIPv6
|
||||
|
||||
maxRetry := 10
|
||||
for {
|
||||
if maxRetry == 0 {
|
||||
log.Println("PrepareDomain maxRetry reached. exiting.")
|
||||
return
|
||||
}
|
||||
|
||||
resolved, err := d.lookupAddr(domainName)
|
||||
if err != nil {
|
||||
maxRetry--
|
||||
log.Printf("PrepareDomain err: %v\n", err)
|
||||
select {
|
||||
case <-closeCh:
|
||||
log.Printf("PrepareDomain exit due to core closed")
|
||||
return
|
||||
case <-time.After(time.Second * 2):
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
d.vServer = resolved
|
||||
log.Printf("Prepare Result:\n Domain: %s\n Port: %d\n IPs: %v\n",
|
||||
resolved.domain, resolved.Port, resolved.IPs)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (d *ProtectedDialer) getFd(network v2net.Network) (fd int, err error) {
|
||||
switch network {
|
||||
case v2net.Network_TCP:
|
||||
fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_STREAM, unix.IPPROTO_TCP)
|
||||
case v2net.Network_UDP:
|
||||
fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP)
|
||||
default:
|
||||
err = fmt.Errorf("unknow network")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Init implement internet.SystemDialer
|
||||
func (d *ProtectedDialer) Init(_ dns.Client, _ outbound.Manager) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// Dial exported as the protected dial method
|
||||
func (d *ProtectedDialer) Dial(ctx context.Context,
|
||||
src v2net.Address, dest v2net.Destination, sockopt *v2internet.SocketConfig) (net.Conn, error) {
|
||||
|
||||
// network := dest.Network.SystemString()
|
||||
Address := dest.NetAddr()
|
||||
|
||||
// v2ray server address,
|
||||
// try to connect fixed IP if multiple IP parsed from domain,
|
||||
// and switch to next IP if error occurred
|
||||
if Address == d.currentServer {
|
||||
if d.vServer == nil {
|
||||
log.Println("Dial pending prepare ...", Address)
|
||||
<-d.resolveChan
|
||||
|
||||
// user may close connection during PrepareDomain,
|
||||
// fast return release resources.
|
||||
if d.vServer == nil {
|
||||
return nil, fmt.Errorf("fail to prepare domain %s", d.currentServer)
|
||||
}
|
||||
}
|
||||
|
||||
// if time.Since(d.vServer.lastResolved) > time.Minute*30 {
|
||||
// go d.PrepareDomain(Address, nil, d.preferIPv6)
|
||||
// }
|
||||
|
||||
fd, err := d.getFd(dest.Network)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
curIP := d.vServer.currentIP()
|
||||
conn, err := d.fdConn(ctx, curIP, d.vServer.Port, dest.Network, fd)
|
||||
if err != nil {
|
||||
d.vServer.NextIP()
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("Using Prepared: %s", curIP)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// v2ray connecting to "domestic" servers, no caching results
|
||||
// log.Printf("Not Using Prepared: %s,%s", network, Address)
|
||||
resolved, err := d.lookupAddr(Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fd, err := d.getFd(dest.Network)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// use the first resolved address.
|
||||
// the result IP may vary, eg: IPv6 addrs comes first if client has ipv6 address
|
||||
return d.fdConn(ctx, resolved.IPs[0], resolved.Port, dest.Network, fd)
|
||||
}
|
||||
|
||||
func (d *ProtectedDialer) DestIpAddress() net.IP {
|
||||
return d.vServer.currentIP()
|
||||
}
|
||||
|
||||
func (d *ProtectedDialer) fdConn(ctx context.Context, ip net.IP, port int, network v2net.Network, fd int) (net.Conn, error) {
|
||||
|
||||
defer unix.Close(fd)
|
||||
|
||||
// call android VPN service to "protect" the fd connecting straight out
|
||||
if !d.Protect(fd) {
|
||||
log.Printf("fdConn fail to protect, Close Fd: %d", fd)
|
||||
return nil, errors.New("fail to protect")
|
||||
}
|
||||
|
||||
sa := &unix.SockaddrInet6{
|
||||
Port: port,
|
||||
}
|
||||
copy(sa.Addr[:], ip.To16())
|
||||
|
||||
if network == v2net.Network_UDP {
|
||||
if err := unix.Bind(fd, &unix.SockaddrInet6{}); err != nil {
|
||||
log.Printf("fdConn unix.Bind err, Close Fd: %d Err: %v", fd, err)
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := unix.Connect(fd, sa); err != nil {
|
||||
// log.Printf("fdConn unix.Connect err, Close Fd: %d Err: %v", fd, err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
file := os.NewFile(uintptr(fd), "Socket")
|
||||
if file == nil {
|
||||
// returned value will be nil if fd is not a valid file descriptor
|
||||
return nil, errors.New("fdConn fd invalid")
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
//Closing conn does not affect file, and closing file does not affect conn.
|
||||
if network == v2net.Network_UDP {
|
||||
packetConn, err := net.FilePacketConn(file)
|
||||
if err != nil {
|
||||
log.Printf("fdConn FilePacketConn Close Fd: %d Err: %v", fd, err)
|
||||
return nil, err
|
||||
}
|
||||
return &v2internet.PacketConnWrapper{
|
||||
Conn: packetConn,
|
||||
Dest: &net.UDPAddr{
|
||||
IP: ip,
|
||||
Port: port,
|
||||
},
|
||||
}, nil
|
||||
} else {
|
||||
conn, err := net.FileConn(file)
|
||||
if err != nil {
|
||||
log.Printf("fdConn FileConn Close Fd: %d Err: %v", fd, err)
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
package libv2ray
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
v2net "github.com/xtls/xray-core/common/net"
|
||||
)
|
||||
|
||||
type fakeSupportSet struct{}
|
||||
|
||||
func (f fakeSupportSet) Protect(int) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func TestProtectedDialer_PrepareDomain(t *testing.T) {
|
||||
type args struct {
|
||||
domainName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
{"", args{"baidu.com:80"}},
|
||||
// {"", args{"cloudflare.com:443"}},
|
||||
// {"", args{"apple.com:443"}},
|
||||
// {"", args{"110.110.110.110:443"}},
|
||||
// {"", args{"[2002:1234::1]:443"}},
|
||||
}
|
||||
d := NewPreotectedDialer(fakeSupportSet{})
|
||||
for _, tt := range tests {
|
||||
ch := make(chan struct{})
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
go d.PrepareDomain(tt.args.domainName, ch, false)
|
||||
|
||||
time.Sleep(time.Second)
|
||||
go d.vServer.NextIP()
|
||||
t.Log(d.vServer.currentIP())
|
||||
})
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
func TestProtectedDialer_Dial(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
{"baidu.com:80", false},
|
||||
{"cloudflare.com:80", false},
|
||||
{"172.16.192.11:80", true},
|
||||
// {"172.16.192.10:80", true},
|
||||
// {"[2fff:4322::1]:443", true},
|
||||
// {"[fc00::1]:443", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ch := make(chan struct{})
|
||||
|
||||
d := NewPreotectedDialer(fakeSupportSet{})
|
||||
d.currentServer = tt.name
|
||||
|
||||
go d.PrepareDomain(tt.name, ch, false)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
dial := func() {
|
||||
defer wg.Done()
|
||||
dest, _ := v2net.ParseDestination("tcp:" + tt.name)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
conn, err := d.Dial(ctx, nil, dest, nil)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
return
|
||||
}
|
||||
_host, _, _ := net.SplitHostPort(tt.name)
|
||||
fmt.Fprintf(conn, fmt.Sprintf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", _host))
|
||||
status, err := bufio.NewReader(conn).ReadString('\n')
|
||||
t.Logf("%#v, %#v\n", status, err)
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
for n := 0; n < 3; n++ {
|
||||
wg.Add(1)
|
||||
go dial()
|
||||
// time.Sleep(time.Millisecond * 10)
|
||||
// d.pendingMap[tt.name] = make(chan struct{})
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_resolved_NextIP(t *testing.T) {
|
||||
type fields struct {
|
||||
domain string
|
||||
IPs []net.IP
|
||||
Port int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
{"test1",
|
||||
fields{
|
||||
domain: "www.baidu.com",
|
||||
IPs: []net.IP{
|
||||
net.ParseIP("1.2.3.4"),
|
||||
net.ParseIP("4.3.2.1"),
|
||||
net.ParseIP("1234::1"),
|
||||
net.ParseIP("4321::1"),
|
||||
},
|
||||
}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &resolved{
|
||||
domain: tt.fields.domain,
|
||||
IPs: tt.fields.IPs,
|
||||
Port: tt.fields.Port,
|
||||
}
|
||||
t.Logf("%v", r.IPs)
|
||||
t.Logf("%v", r.currentIP())
|
||||
r.NextIP()
|
||||
t.Logf("%v", r.currentIP())
|
||||
r.NextIP()
|
||||
t.Logf("%v", r.currentIP())
|
||||
r.NextIP()
|
||||
t.Logf("%v", r.currentIP())
|
||||
time.Sleep(3 * time.Second)
|
||||
r.NextIP()
|
||||
t.Logf("%v", r.currentIP())
|
||||
time.Sleep(5 * time.Second)
|
||||
r.NextIP()
|
||||
t.Logf("%v", r.currentIP())
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue