├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE ├── README.md ├── assets ├── geoip.dat └── geosite.dat ├── gen_assets.sh ├── go.mod ├── go.sum ├── libv2ray_main.go ├── libv2ray_support.go └── libv2ray_support_test.go /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | release_tag: 7 | required: true 8 | type: string 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | 17 | 18 | - name: Setup Golang 19 | uses: actions/setup-go@v5 20 | with: 21 | go-version-file: 'go.mod' 22 | 23 | - name: Install gomobile 24 | run: | 25 | go install golang.org/x/mobile/cmd/gomobile@latest 26 | export PATH=$PATH:~/go/bin 27 | 28 | - name: Setup Java 29 | uses: actions/setup-java@v4 30 | with: 31 | distribution: 'temurin' 32 | java-version: '21' 33 | 34 | - name: Setup Android SDK Tools 35 | uses: android-actions/setup-android@v3.2.2 36 | with: 37 | cmdline-tools-version: 10406996 38 | 39 | - name: Setup Android NDK 40 | uses: nttld/setup-ndk@v1.5.0 41 | with: 42 | ndk-version: 'r27b' 43 | link-to-sdk: true 44 | 45 | - name: Build 46 | run: | 47 | mkdir -p assets data 48 | bash gen_assets.sh download 49 | cp -v data/*.dat assets/ 50 | gomobile init 51 | go mod tidy 52 | gomobile bind -v -androidapi 21 -ldflags='-s -w' ./ 53 | - name: Upload AndroidLibXrayLite to release 54 | uses: svenstaro/upload-release-action@v2 55 | with: 56 | file: ./libv2ray*r 57 | tag: ${{ github.event.inputs.release_tag }} 58 | file_glob: true 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | gradle/build.gradle 3 | *.pb.go 4 | binary*.go 5 | conf/demo/ 6 | 7 | demo/ 8 | assets/ 9 | libv2ray*.[a|j]ar 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidLibXrayLite 2 | 3 | ## Build requirements 4 | * JDK 5 | * Android SDK 6 | * Go 7 | * gomobile 8 | 9 | ## Build instructions 10 | 1. `git clone [repo] && cd AndroidLibXrayLite` 11 | 2. `gomobile init` 12 | 3. `go mod tidy -v` 13 | 4. `gomobile bind -v -androidapi 19 -ldflags='-s -w' ./` 14 | -------------------------------------------------------------------------------- /assets/geoip.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GFW-knocker/AndroidLibXrayLite/13a12c92e16b8f82be2945aadedc1f87b11b2ca6/assets/geoip.dat -------------------------------------------------------------------------------- /assets/geosite.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GFW-knocker/AndroidLibXrayLite/13a12c92e16b8f82be2945aadedc1f87b11b2ca6/assets/geosite.dat -------------------------------------------------------------------------------- /gen_assets.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | # Set magic variables for current file & dir 8 | __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 9 | __file="${__dir}/$(basename "${BASH_SOURCE[0]}")" 10 | __base="$(basename ${__file} .sh)" 11 | 12 | 13 | DATADIR=${__dir}/data 14 | 15 | compile_dat () { 16 | local TMPDIR=$(mktemp -d) 17 | 18 | trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; rm -rf $TMPDIR; trap ERR; exit 1' ERR 19 | 20 | local GEOSITE=${GOPATH}/src/github.com/v2ray/domain-list-community 21 | if [[ -d ${GEOSITE} ]]; then 22 | cd ${GEOSITE} && git pull 23 | else 24 | mkdir -p ${GEOSITE} 25 | cd ${GEOSITE} && git clone https://github.com/v2ray/domain-list-community.git . 26 | fi 27 | go run main.go 28 | 29 | if [[ -e dlc.dat ]]; then 30 | rm -f $DATADIR/geosite.dat 31 | mv dlc.dat $DATADIR/geosite.dat 32 | echo "----------> geosite.dat updated." 33 | else 34 | echo "----------> geosite.dat failed to update." 35 | fi 36 | 37 | 38 | if [[ ! -x $GOPATH/bin/geoip ]]; then 39 | go get -v -u github.com/v2ray/geoip 40 | fi 41 | 42 | cd $TMPDIR 43 | curl -L -O http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country-CSV.zip 44 | unzip -q GeoLite2-Country-CSV.zip 45 | mkdir geoip && find . -name '*.csv' -exec mv -t ./geoip {} + 46 | $GOPATH/bin/geoip \ 47 | --country=./geoip/GeoLite2-Country-Locations-en.csv \ 48 | --ipv4=./geoip/GeoLite2-Country-Blocks-IPv4.csv \ 49 | --ipv6=./geoip/GeoLite2-Country-Blocks-IPv6.csv 50 | 51 | if [[ -e geoip.dat ]]; then 52 | rm -f $DATADIR/geoip.dat 53 | mv ./geoip.dat $DATADIR/geoip.dat 54 | echo "----------> geoip.dat updated." 55 | else 56 | echo "----------> geoip.dat failed to update." 57 | fi 58 | trap ERR 59 | return 0 60 | } 61 | 62 | 63 | download_dat () { 64 | wget -qO - https://api.github.com/repos/v2ray/geoip/releases/latest \ 65 | | grep browser_download_url | cut -d '"' -f 4 \ 66 | | wget -i - -O $DATADIR/geoip.dat 67 | 68 | wget -qO - https://api.github.com/repos/v2ray/domain-list-community/releases/latest \ 69 | | grep browser_download_url | cut -d '"' -f 4 \ 70 | | wget -i - -O $DATADIR/geosite.dat 71 | } 72 | 73 | ACTION="${1:-}" 74 | if [[ -z $ACTION ]]; then 75 | ACTION=download 76 | fi 77 | 78 | case $ACTION in 79 | "download") download_dat;; 80 | "compile") compile_dat;; 81 | esac 82 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/GFW-knocker/AndroidLibXrayLite 2 | 3 | go 1.24.2 4 | 5 | require ( 6 | github.com/GFW-knocker/Xray-core v1.25.4-mahsa-r2 7 | golang.org/x/mobile v0.0.0-20250506005352-78cd7a343bde 8 | golang.org/x/sys v0.33.0 9 | ) 10 | 11 | require ( 12 | github.com/GFW-knocker/wireguard v1.0.6 // indirect 13 | github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 // indirect 14 | github.com/andybalholm/brotli v1.1.1 // indirect 15 | github.com/cloudflare/circl v1.6.1 // indirect 16 | github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 // indirect 17 | github.com/francoispqt/gojay v1.2.13 // indirect 18 | github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // indirect 19 | github.com/go-task/slim-sprig/v3 v3.0.0 // indirect 20 | github.com/google/btree v1.1.3 // indirect 21 | github.com/google/pprof v0.0.0-20250501235452-c0086092b71a // indirect 22 | github.com/gorilla/websocket v1.5.3 // indirect 23 | github.com/klauspost/compress v1.18.0 // indirect 24 | github.com/klauspost/cpuid/v2 v2.2.10 // indirect 25 | github.com/onsi/ginkgo/v2 v2.23.4 // indirect 26 | github.com/pelletier/go-toml v1.9.5 // indirect 27 | github.com/pires/go-proxyproto v0.8.1 // indirect 28 | github.com/quic-go/qpack v0.5.1 // indirect 29 | github.com/quic-go/quic-go v0.51.0 // indirect 30 | github.com/refraction-networking/utls v1.7.2 // indirect 31 | github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect 32 | github.com/sagernet/sing v0.6.9 // indirect 33 | github.com/sagernet/sing-shadowsocks v0.2.7 // indirect 34 | github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // indirect 35 | github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect 36 | github.com/vishvananda/netlink v1.3.1 // indirect 37 | github.com/vishvananda/netns v0.0.5 // indirect 38 | github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d // indirect 39 | go.uber.org/automaxprocs v1.6.0 // indirect 40 | go.uber.org/mock v0.5.2 // indirect 41 | go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect 42 | golang.org/x/crypto v0.38.0 // indirect 43 | golang.org/x/mod v0.24.0 // indirect 44 | golang.org/x/net v0.40.0 // indirect 45 | golang.org/x/sync v0.14.0 // indirect 46 | golang.org/x/text v0.25.0 // indirect 47 | golang.org/x/time v0.11.0 // indirect 48 | golang.org/x/tools v0.33.0 // indirect 49 | golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect 50 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 // indirect 51 | google.golang.org/grpc v1.72.0 // indirect 52 | google.golang.org/protobuf v1.36.6 // indirect 53 | gopkg.in/yaml.v2 v2.4.0 // indirect 54 | gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5 // indirect 55 | lukechampine.com/blake3 v1.4.1 // indirect 56 | ) 57 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 4 | cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= 5 | dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= 6 | dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= 7 | dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= 8 | dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= 9 | git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= 10 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 11 | github.com/GFW-knocker/Xray-core v1.25.4-mahsa-r2 h1:j0xH0iCGGVByZtKFe37WZl+wnP7OYHaRctxb2Qvl4W8= 12 | github.com/GFW-knocker/Xray-core v1.25.4-mahsa-r2/go.mod h1:Tyzyf+J7nCL0l8Kg5qlfEXYed2758I3/qTzqNqvW/BA= 13 | github.com/GFW-knocker/wireguard v1.0.6 h1:zOqzFVlj1QcMjA4Q0Q34NfSGG+cAXIrYRxcBouE4vGU= 14 | github.com/GFW-knocker/wireguard v1.0.6/go.mod h1:lkzRoCibuOeA2/KOpo1AnppBT+v1uY/NNH0Nc2uBsEM= 15 | github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 h1:Wo41lDOevRJSGpevP+8Pk5bANX7fJacO2w04aqLiC5I= 16 | github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM= 17 | github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= 18 | github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= 19 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= 20 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 21 | github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= 22 | github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= 23 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 24 | github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= 25 | github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= 26 | github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 27 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 28 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 29 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 30 | github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= 31 | github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 h1:ucRHb6/lvW/+mTEIGbvhcYU3S8+uSNkuMjx/qZFfhtM= 32 | github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= 33 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 34 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 35 | github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= 36 | github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= 37 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 38 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 39 | github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4= 40 | github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= 41 | github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 42 | github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= 43 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 44 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 45 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 46 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 47 | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= 48 | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 49 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 50 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 51 | github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= 52 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 53 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 54 | github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U= 55 | github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs= 56 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 57 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 58 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 59 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 60 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 61 | github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= 62 | github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= 63 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 64 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 65 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 66 | github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= 67 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 68 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 69 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 70 | github.com/google/pprof v0.0.0-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4= 71 | github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= 72 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 73 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 74 | github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= 75 | github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= 76 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 77 | github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= 78 | github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 79 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 80 | github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= 81 | github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= 82 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 83 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 84 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 85 | github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= 86 | github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= 87 | github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= 88 | github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= 89 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 90 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 91 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 92 | github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 93 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 94 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 95 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 96 | github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= 97 | github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 98 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 99 | github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= 100 | github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= 101 | github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= 102 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 103 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 104 | github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= 105 | github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= 106 | github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= 107 | github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= 108 | github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= 109 | github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= 110 | github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= 111 | github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= 112 | github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 113 | github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0= 114 | github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= 115 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 116 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 117 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 118 | github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= 119 | github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= 120 | github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 121 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 122 | github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 123 | github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 124 | github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= 125 | github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= 126 | github.com/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc= 127 | github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ= 128 | github.com/refraction-networking/utls v1.7.2 h1:XOgYzit7lAKaa7kzAO5BJR9l4X/H200eVUD4s8SF8/s= 129 | github.com/refraction-networking/utls v1.7.2/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ= 130 | github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= 131 | github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= 132 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 133 | github.com/sagernet/sing v0.6.9 h1:y/XJH17oyBd6hxgQtKnIdLXu7TsOHxO5i1JeVfVmjXw= 134 | github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= 135 | github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8= 136 | github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE= 137 | github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4= 138 | github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg= 139 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 140 | github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= 141 | github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= 142 | github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= 143 | github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= 144 | github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= 145 | github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= 146 | github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= 147 | github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= 148 | github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= 149 | github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= 150 | github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= 151 | github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= 152 | github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= 153 | github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= 154 | github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= 155 | github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= 156 | github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= 157 | github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= 158 | github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= 159 | github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 160 | github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= 161 | github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= 162 | github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= 163 | github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= 164 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 165 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 166 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 167 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 168 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 169 | github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= 170 | github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI= 171 | github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU= 172 | github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= 173 | github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= 174 | github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= 175 | github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= 176 | github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= 177 | github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= 178 | github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d h1:+B97uD9uHLgAAulhigmys4BVwZZypzK7gPN3WtpgRJg= 179 | github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE= 180 | github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= 181 | github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= 182 | go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= 183 | go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= 184 | go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= 185 | go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= 186 | go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= 187 | go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= 188 | go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= 189 | go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= 190 | go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= 191 | go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= 192 | go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= 193 | go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= 194 | go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= 195 | go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= 196 | go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= 197 | go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= 198 | go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= 199 | go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= 200 | go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= 201 | go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= 202 | golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= 203 | golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 204 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 205 | golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 206 | golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= 207 | golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= 208 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 209 | golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 210 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 211 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 212 | golang.org/x/mobile v0.0.0-20250506005352-78cd7a343bde h1:klxJIHHSTIJItwhIaVT2EydFn+8urdz5cnisEO0Uy6I= 213 | golang.org/x/mobile v0.0.0-20250506005352-78cd7a343bde/go.mod h1:T9M84Yhr+nZUSLopZMA95xrVLgn6hC6YwibPkqR8/hw= 214 | golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= 215 | golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= 216 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 217 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 218 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 219 | golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 220 | golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 221 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 222 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 223 | golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 224 | golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= 225 | golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= 226 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 227 | golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 228 | golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 229 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 230 | golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 231 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 232 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 233 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 234 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 235 | golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= 236 | golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 237 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 238 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 239 | golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 240 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 241 | golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 242 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 243 | golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 244 | golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= 245 | golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 246 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 247 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 248 | golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= 249 | golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= 250 | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 251 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 252 | golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= 253 | golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= 254 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 255 | golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 256 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 257 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 258 | golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= 259 | golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= 260 | golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= 261 | golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= 262 | google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= 263 | google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= 264 | google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= 265 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 266 | google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 267 | google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 268 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 269 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 270 | google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 271 | google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 272 | google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= 273 | google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 274 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 h1:IkAfh6J/yllPtpYFU0zZN1hUPYdT0ogkBT/9hMxHjvg= 275 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= 276 | google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 277 | google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= 278 | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 279 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 280 | google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM= 281 | google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= 282 | google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= 283 | google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 284 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 285 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 286 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 287 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 288 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 289 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 290 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 291 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 292 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 293 | gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 294 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 295 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 296 | grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= 297 | gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5 h1:sfK5nHuG7lRFZ2FdTT3RimOqWBg8IrVm+/Vko1FVOsk= 298 | gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g= 299 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 300 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 301 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 302 | lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= 303 | lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= 304 | sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= 305 | sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= 306 | -------------------------------------------------------------------------------- /libv2ray_main.go: -------------------------------------------------------------------------------- 1 | package libv2ray 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | "context" 7 | "crypto/tls" 8 | "errors" 9 | "fmt" 10 | "io" 11 | "log" 12 | "math/rand" 13 | "net" 14 | "net/http" 15 | "net/url" 16 | "os" 17 | "path/filepath" 18 | "strings" 19 | "sync" 20 | "time" 21 | 22 | mobasset "golang.org/x/mobile/asset" 23 | 24 | v2net "github.com/GFW-knocker/Xray-core/common/net" 25 | v2filesystem "github.com/GFW-knocker/Xray-core/common/platform/filesystem" 26 | v2core "github.com/GFW-knocker/Xray-core/core" 27 | v2stats "github.com/GFW-knocker/Xray-core/features/stats" 28 | v2serial "github.com/GFW-knocker/Xray-core/infra/conf/serial" 29 | _ "github.com/GFW-knocker/Xray-core/main/distro/all" 30 | v2internet "github.com/GFW-knocker/Xray-core/transport/internet" 31 | 32 | v2applog "github.com/GFW-knocker/Xray-core/app/log" 33 | v2commlog "github.com/GFW-knocker/Xray-core/common/log" 34 | ) 35 | 36 | const ( 37 | v2Asset = "xray.location.asset" 38 | xudpBaseKey = "xray.xudp.basekey" 39 | ) 40 | 41 | /* 42 | V2RayPoint V2Ray Point Server 43 | This is territory of Go, so no getter and setters! 44 | */ 45 | type V2RayPoint struct { 46 | SupportSet V2RayVPNServiceSupportsSet 47 | statsManager v2stats.Manager 48 | 49 | dialer *ProtectedDialer 50 | v2rayOP sync.Mutex 51 | closeChan chan struct{} 52 | 53 | Vpoint *v2core.Instance 54 | IsRunning bool 55 | 56 | DomainName string 57 | ConfigureFileContent string 58 | AsyncResolve bool 59 | } 60 | 61 | /*V2RayVPNServiceSupportsSet To support Android VPN mode*/ 62 | type V2RayVPNServiceSupportsSet interface { 63 | Setup(Conf string) int 64 | Prepare() int 65 | Shutdown() int 66 | Protect(int) bool 67 | OnEmitStatus(int, string) int 68 | } 69 | 70 | /*RunLoop Run V2Ray main loop 71 | */ 72 | func (v *V2RayPoint) RunLoop(prefIPv6 bool) (err error) { 73 | v.v2rayOP.Lock() 74 | defer v.v2rayOP.Unlock() 75 | //Construct Context 76 | 77 | if !v.IsRunning { 78 | v.closeChan = make(chan struct{}) 79 | v.dialer.PrepareResolveChan() 80 | go func() { 81 | select { 82 | // wait until resolved 83 | case <-v.dialer.ResolveChan(): 84 | // shutdown VPNService if server name can not reolved 85 | if !v.dialer.IsVServerReady() { 86 | log.Println("vServer cannot resolved, shutdown") 87 | v.StopLoop() 88 | v.SupportSet.Shutdown() 89 | } 90 | 91 | // stop waiting if manually closed 92 | case <-v.closeChan: 93 | } 94 | }() 95 | 96 | if v.AsyncResolve { 97 | go func() { 98 | v.dialer.PrepareDomain(v.DomainName, v.closeChan, prefIPv6) 99 | close(v.dialer.ResolveChan()) 100 | }() 101 | } else { 102 | v.dialer.PrepareDomain(v.DomainName, v.closeChan, prefIPv6) 103 | close(v.dialer.ResolveChan()) 104 | } 105 | 106 | err = v.pointloop() 107 | } 108 | return 109 | } 110 | 111 | /*StopLoop Stop V2Ray main loop 112 | */ 113 | func (v *V2RayPoint) StopLoop() (err error) { 114 | v.v2rayOP.Lock() 115 | defer v.v2rayOP.Unlock() 116 | if v.IsRunning { 117 | close(v.closeChan) 118 | v.shutdownInit() 119 | v.SupportSet.OnEmitStatus(0, "Closed") 120 | } 121 | return 122 | } 123 | 124 | func (v *V2RayPoint) TerminateByExit() { 125 | log.Println("OS Exit called") 126 | os.Exit(0) 127 | } 128 | 129 | type WebResult struct { 130 | RespBody string 131 | RespHeader string 132 | RespError string 133 | } 134 | 135 | //export GetDataFromWeb *WebResult 136 | func GetDataFromWeb(myurl string, mydata string, my_proxy string, mytimeout int, allow_sscrt bool, is_post bool, is_CF_API bool) *WebResult { 137 | 138 | uagent_list := []string{ 139 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", 140 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0", 141 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1 Safari/605.1.15", 142 | "Mozilla/5.0 (iPhone; CPU iPhone OS 17_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Mobile/15E148 Safari/604.1", 143 | "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36", 144 | } 145 | 146 | uaindex := rand.Intn(len(uagent_list)) 147 | uagent_string := uagent_list[uaindex] 148 | 149 | trp := &http.Transport{ 150 | TLSClientConfig: &tls.Config{ 151 | InsecureSkipVerify: allow_sscrt, 152 | }, 153 | } 154 | 155 | if my_proxy != "" { 156 | proxyURL, err := url.Parse(my_proxy) 157 | if err != nil { 158 | return &WebResult{"", "", err.Error()} 159 | } 160 | trp.Proxy = http.ProxyURL(proxyURL) 161 | } 162 | 163 | client := &http.Client{ 164 | Transport: trp, 165 | Timeout: time.Duration(mytimeout) * time.Millisecond, 166 | } 167 | 168 | var req *http.Request 169 | 170 | if is_post { 171 | var err error 172 | req, err = http.NewRequest("POST", myurl, bytes.NewBuffer([]byte(mydata))) 173 | if err != nil { 174 | return &WebResult{"", "", err.Error()} 175 | } 176 | 177 | if is_CF_API { 178 | req.Header.Set("Content-Type", "application/json; charset=UTF-8") 179 | req.Header.Set("Host", "api.cloudflareclient.com") 180 | req.Header.Set("Connection", "Keep-Alive") 181 | req.Header.Set("Accept-Encoding", "gzip") 182 | req.Header.Set("User-Agent", "okhttp/3.12.1") 183 | req.Header.Set("CF-Client-Version", "a-6.30-3596") 184 | } else { 185 | req.Header.Set("Content-Type", "application/json") 186 | req.Header.Set("Accept", "application/json") 187 | req.Header.Set("User-Agent", uagent_string) 188 | } 189 | 190 | } else { 191 | var err error 192 | req, err = http.NewRequest("GET", myurl, nil) 193 | req.Header.Set("User-Agent", uagent_string) 194 | if err != nil { 195 | return &WebResult{"", "", err.Error()} 196 | } 197 | 198 | } 199 | 200 | resp, err := client.Do(req) 201 | if err != nil { 202 | return &WebResult{"", "", err.Error()} 203 | } 204 | defer resp.Body.Close() 205 | 206 | resp_header := resp.Header.Get("X-From-Server") 207 | 208 | var resp_body = "" 209 | 210 | if resp.Header.Get("Content-Encoding") == "gzip" { 211 | // Create a new GZIP reader 212 | reader, err := gzip.NewReader(resp.Body) 213 | if err != nil { 214 | return &WebResult{"", "", err.Error()} 215 | } 216 | defer reader.Close() 217 | 218 | body, err := io.ReadAll(reader) 219 | if err != nil { 220 | return &WebResult{"", "", err.Error()} 221 | } 222 | resp_body = string(body) 223 | 224 | } else { 225 | body, err := io.ReadAll(resp.Body) 226 | if err != nil { 227 | return &WebResult{"", "", err.Error()} 228 | } 229 | resp_body = string(body) 230 | } 231 | 232 | if resp.StatusCode > 299 { 233 | return &WebResult{"", resp_header, fmt.Sprintf("%s %d\n%s", "ERR status code:", resp.StatusCode, resp_body)} 234 | } 235 | 236 | return &WebResult{resp_body, resp_header, ""} 237 | } 238 | 239 | // Delegate Funcation 240 | func (v V2RayPoint) QueryStats(tag string, direct string) int64 { 241 | if v.statsManager == nil { 242 | return 0 243 | } 244 | counter := v.statsManager.GetCounter(fmt.Sprintf("outbound>>>%s>>>traffic>>>%s", tag, direct)) 245 | if counter == nil { 246 | return 0 247 | } 248 | return counter.Set(0) 249 | } 250 | 251 | func (v *V2RayPoint) shutdownInit() { 252 | v.IsRunning = false 253 | v.Vpoint.Close() 254 | v.Vpoint = nil 255 | v.statsManager = nil 256 | } 257 | 258 | func (v *V2RayPoint) pointloop() error { 259 | log.Println("loading core config") 260 | config, err := v2serial.LoadJSONConfig(strings.NewReader(v.ConfigureFileContent)) 261 | if err != nil { 262 | log.Println(err) 263 | return err 264 | } 265 | 266 | log.Println("new core") 267 | v.Vpoint, err = v2core.New(config) 268 | if err != nil { 269 | v.Vpoint = nil 270 | log.Println(err) 271 | return err 272 | } 273 | v.statsManager = v.Vpoint.GetFeature(v2stats.ManagerType()).(v2stats.Manager) 274 | 275 | log.Println("start core") 276 | v.IsRunning = true 277 | if err := v.Vpoint.Start(); err != nil { 278 | v.IsRunning = false 279 | log.Println(err) 280 | return err 281 | } 282 | 283 | v.SupportSet.Prepare() 284 | v.SupportSet.Setup("") 285 | v.SupportSet.OnEmitStatus(0, "Running") 286 | return nil 287 | } 288 | 289 | func (v *V2RayPoint) MeasureDelay(url string) (int64, error) { 290 | ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second) 291 | 292 | go func() { 293 | select { 294 | case <-v.closeChan: 295 | // cancel request if close called during meansure 296 | cancel() 297 | case <-ctx.Done(): 298 | } 299 | }() 300 | 301 | return measureInstDelay(ctx, v.Vpoint, url) 302 | } 303 | 304 | // InitV2Env set v2 asset path 305 | func InitV2Env(envPath string, key string) { 306 | //Initialize asset API, Since Raymond Will not let notify the asset location inside Process, 307 | //We need to set location outside V2Ray 308 | if len(envPath) > 0 { 309 | os.Setenv(v2Asset, envPath) 310 | } 311 | if len(key) > 0 { 312 | os.Setenv(xudpBaseKey, key) 313 | } 314 | 315 | //Now we handle read, fallback to gomobile asset (apk assets) 316 | v2filesystem.NewFileReader = func(path string) (io.ReadCloser, error) { 317 | if _, err := os.Stat(path); os.IsNotExist(err) { 318 | _, file := filepath.Split(path) 319 | return mobasset.Open(file) 320 | } 321 | return os.Open(path) 322 | } 323 | } 324 | 325 | func MeasureOutboundDelay(ConfigureFileContent string, url string) (int64, error) { 326 | 327 | config, err := v2serial.LoadJSONConfig(strings.NewReader(ConfigureFileContent)) 328 | if err != nil { 329 | return -1, err 330 | } 331 | 332 | // dont listen to anything for test purpose 333 | config.Inbound = nil 334 | // config.App: (fakedns), log, dispatcher, InboundConfig, OutboundConfig, (stats), router, dns, (policy) 335 | // keep only basic features 336 | config.App = config.App[:5] 337 | 338 | inst, err := v2core.New(config) 339 | if err != nil { 340 | return -1, err 341 | } 342 | 343 | inst.Start() 344 | delay, err := measureInstDelay(context.Background(), inst, url) 345 | inst.Close() 346 | return delay, err 347 | } 348 | 349 | /*NewV2RayPoint new V2RayPoint*/ 350 | func NewV2RayPoint(s V2RayVPNServiceSupportsSet, adns bool) *V2RayPoint { 351 | // inject our own log writer 352 | v2applog.RegisterHandlerCreator(v2applog.LogType_Console, 353 | func(lt v2applog.LogType, 354 | options v2applog.HandlerCreatorOptions) (v2commlog.Handler, error) { 355 | return v2commlog.NewLogger(createStdoutLogWriter()), nil 356 | }) 357 | 358 | dialer := NewPreotectedDialer(s) 359 | v2internet.UseAlternativeSystemDialer(dialer) 360 | return &V2RayPoint{ 361 | SupportSet: s, 362 | dialer: dialer, 363 | AsyncResolve: adns, 364 | } 365 | } 366 | 367 | /* 368 | CheckVersionX string 369 | This func will return libv2ray binding version and V2Ray version used. 370 | */ 371 | func CheckVersionX() string { 372 | return fmt.Sprintf("Mahsa-XrayCore v%s-r1", v2core.Version()) 373 | } 374 | 375 | func measureInstDelay(ctx context.Context, inst *v2core.Instance, url string) (int64, error) { 376 | if inst == nil { 377 | return -1, errors.New("core instance nil") 378 | } 379 | 380 | tr := &http.Transport{ 381 | TLSHandshakeTimeout: 6 * time.Second, 382 | DisableKeepAlives: true, 383 | DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { 384 | dest, err := v2net.ParseDestination(fmt.Sprintf("%s:%s", network, addr)) 385 | if err != nil { 386 | return nil, err 387 | } 388 | return v2core.Dial(ctx, inst, dest) 389 | }, 390 | } 391 | 392 | c := &http.Client{ 393 | Transport: tr, 394 | Timeout: 12 * time.Second, 395 | } 396 | 397 | if len(url) <= 0 { 398 | url = "https://www.google.com/generate_204" 399 | } 400 | req, _ := http.NewRequestWithContext(ctx, "GET", url, nil) 401 | start := time.Now() 402 | resp, err := c.Do(req) 403 | if err != nil { 404 | return -1, err 405 | } 406 | 407 | if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent { 408 | return -1, fmt.Errorf("status != 20x: %s", resp.Status) 409 | } 410 | resp.Body.Close() 411 | return time.Since(start).Milliseconds(), nil 412 | } 413 | 414 | // This struct creates our own log writer without datatime stamp 415 | // As Android adds time stamps on each line 416 | type consoleLogWriter struct { 417 | logger *log.Logger 418 | } 419 | 420 | func (w *consoleLogWriter) Write(s string) error { 421 | w.logger.Print(s) 422 | return nil 423 | } 424 | 425 | func (w *consoleLogWriter) Close() error { 426 | return nil 427 | } 428 | 429 | // This logger won't print data/time stamps 430 | func createStdoutLogWriter() v2commlog.WriterCreator { 431 | return func() v2commlog.Writer { 432 | return &consoleLogWriter{ 433 | logger: log.New(os.Stdout, "", 0)} 434 | } 435 | } 436 | -------------------------------------------------------------------------------- /libv2ray_support.go: -------------------------------------------------------------------------------- 1 | package libv2ray 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "log" 8 | "net" 9 | "os" 10 | "sync" 11 | "time" 12 | 13 | v2net "github.com/GFW-knocker/Xray-core/common/net" 14 | "github.com/GFW-knocker/Xray-core/features/dns" 15 | "github.com/GFW-knocker/Xray-core/features/outbound" 16 | v2internet "github.com/GFW-knocker/Xray-core/transport/internet" 17 | "golang.org/x/sys/unix" 18 | ) 19 | 20 | type protectSet interface { 21 | Protect(int) bool 22 | } 23 | 24 | type resolved struct { 25 | domain string 26 | IPs []net.IP 27 | Port int 28 | lastResolved time.Time 29 | ipIdx uint8 30 | ipLock sync.Mutex 31 | lastSwitched time.Time 32 | } 33 | 34 | // NextIP switch to another resolved result. 35 | // there still be race-condition here if multiple err concurently occured 36 | // may cause idx keep switching, 37 | // but that's an outside error can hardly handled here 38 | func (r *resolved) NextIP() { 39 | r.ipLock.Lock() 40 | defer r.ipLock.Unlock() 41 | 42 | if len(r.IPs) > 1 { 43 | 44 | // throttle, don't switch too quickly 45 | now := time.Now() 46 | if now.Sub(r.lastSwitched) < time.Second*5 { 47 | log.Println("switch too quickly") 48 | return 49 | } 50 | r.lastSwitched = now 51 | r.ipIdx++ 52 | 53 | } else { 54 | return 55 | } 56 | 57 | if r.ipIdx >= uint8(len(r.IPs)) { 58 | r.ipIdx = 0 59 | } 60 | 61 | log.Printf("switched to next IP: %v", r.IPs[r.ipIdx]) 62 | } 63 | 64 | func (r *resolved) currentIP() net.IP { 65 | if r == nil { 66 | return nil 67 | } 68 | 69 | r.ipLock.Lock() 70 | defer r.ipLock.Unlock() 71 | 72 | if r.IPs == nil || len(r.IPs) == 0 { 73 | return nil 74 | } 75 | 76 | if len(r.IPs) > 0 { 77 | return r.IPs[r.ipIdx] 78 | } 79 | 80 | return nil 81 | } 82 | 83 | // NewPreotectedDialer ... 84 | func NewPreotectedDialer(p protectSet) *ProtectedDialer { 85 | d := &ProtectedDialer{ 86 | // prefer native lookup on Android 87 | resolver: &net.Resolver{PreferGo: false}, 88 | protectSet: p, 89 | } 90 | return d 91 | } 92 | 93 | // ProtectedDialer ... 94 | type ProtectedDialer struct { 95 | currentServer string 96 | resolveChan chan struct{} 97 | preferIPv6 bool 98 | 99 | vServer *resolved 100 | resolver *net.Resolver 101 | 102 | protectSet 103 | } 104 | 105 | func (d *ProtectedDialer) IsVServerReady() bool { 106 | return (d.vServer != nil) 107 | } 108 | 109 | func (d *ProtectedDialer) PrepareResolveChan() { 110 | d.resolveChan = make(chan struct{}) 111 | } 112 | 113 | func (d *ProtectedDialer) ResolveChan() chan struct{} { 114 | return d.resolveChan 115 | } 116 | 117 | // simplicated version of golang: internetAddrList in src/net/ipsock.go 118 | func (d *ProtectedDialer) lookupAddr(addr string) (*resolved, error) { 119 | 120 | var ( 121 | err error 122 | host, port string 123 | portnum int 124 | ) 125 | 126 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 127 | defer cancel() 128 | 129 | if host, port, err = net.SplitHostPort(addr); err != nil { 130 | log.Printf("PrepareDomain SplitHostPort Err: %v", err) 131 | return nil, err 132 | } 133 | 134 | if portnum, err = d.resolver.LookupPort(ctx, "tcp", port); err != nil { 135 | log.Printf("PrepareDomain LookupPort Err: %v", err) 136 | return nil, err 137 | } 138 | 139 | addrs, err := d.resolver.LookupIPAddr(ctx, host) 140 | if err != nil { 141 | return nil, err 142 | } 143 | if len(addrs) == 0 { 144 | return nil, fmt.Errorf("domain %s Failed to resolve", addr) 145 | } 146 | 147 | IPs := make([]net.IP, 0) 148 | //ipv6 is prefer, append ipv6 then ipv4 149 | //ipv6 is not prefer, append ipv4 then ipv6 150 | if d.preferIPv6 { 151 | for _, ia := range addrs { 152 | if ia.IP.To4() == nil { 153 | IPs = append(IPs, ia.IP) 154 | } 155 | } 156 | } 157 | for _, ia := range addrs { 158 | if ia.IP.To4() != nil { 159 | IPs = append(IPs, ia.IP) 160 | } 161 | } 162 | if !d.preferIPv6 { 163 | for _, ia := range addrs { 164 | if ia.IP.To4() == nil { 165 | IPs = append(IPs, ia.IP) 166 | } 167 | } 168 | } 169 | 170 | rs := &resolved{ 171 | domain: host, 172 | IPs: IPs, 173 | Port: portnum, 174 | lastResolved: time.Now(), 175 | } 176 | 177 | return rs, nil 178 | } 179 | 180 | // PrepareDomain caches direct v2ray server host 181 | func (d *ProtectedDialer) PrepareDomain(domainName string, closeCh <-chan struct{}, prefIPv6 bool) { 182 | log.Printf("Preparing Domain: %s", domainName) 183 | d.currentServer = domainName 184 | d.preferIPv6 = prefIPv6 185 | 186 | maxRetry := 10 187 | for { 188 | if maxRetry == 0 { 189 | log.Println("PrepareDomain maxRetry reached. exiting.") 190 | return 191 | } 192 | 193 | resolved, err := d.lookupAddr(domainName) 194 | if err != nil { 195 | maxRetry-- 196 | log.Printf("PrepareDomain err: %v\n", err) 197 | select { 198 | case <-closeCh: 199 | log.Printf("PrepareDomain exit due to core closed") 200 | return 201 | case <-time.After(time.Second * 2): 202 | } 203 | continue 204 | } 205 | 206 | d.vServer = resolved 207 | log.Printf("Prepare Result:\n Domain: %s\n Port: %d\n IPs: %v\n", 208 | resolved.domain, resolved.Port, resolved.IPs) 209 | return 210 | } 211 | } 212 | 213 | func (d *ProtectedDialer) getFd(network v2net.Network) (fd int, err error) { 214 | switch network { 215 | case v2net.Network_TCP: 216 | fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_STREAM, unix.IPPROTO_TCP) 217 | case v2net.Network_UDP: 218 | fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP) 219 | default: 220 | err = fmt.Errorf("unknow network") 221 | } 222 | return 223 | } 224 | 225 | // Init implement internet.SystemDialer 226 | func (d *ProtectedDialer) Init(_ dns.Client, _ outbound.Manager) { 227 | // do nothing 228 | } 229 | 230 | // Dial exported as the protected dial method 231 | func (d *ProtectedDialer) Dial(ctx context.Context, 232 | src v2net.Address, dest v2net.Destination, sockopt *v2internet.SocketConfig) (net.Conn, error) { 233 | 234 | network := dest.Network.SystemString() 235 | Address := dest.NetAddr() 236 | 237 | // v2ray server address, 238 | // try to connect fixed IP if multiple IP parsed from domain, 239 | // and switch to next IP if error occurred 240 | if Address == d.currentServer { 241 | if d.vServer == nil { 242 | log.Println("Dial pending prepare ...", Address) 243 | <-d.resolveChan 244 | 245 | // user may close connection during PrepareDomain, 246 | // fast return release resources. 247 | if d.vServer == nil { 248 | return nil, fmt.Errorf("fail to prepare domain %s", d.currentServer) 249 | } 250 | } 251 | 252 | // if time.Since(d.vServer.lastResolved) > time.Minute*30 { 253 | // go d.PrepareDomain(Address, nil, d.preferIPv6) 254 | // } 255 | 256 | fd, err := d.getFd(dest.Network) 257 | if err != nil { 258 | return nil, err 259 | } 260 | 261 | curIP := d.vServer.currentIP() 262 | conn, err := d.fdConn(ctx, curIP, d.vServer.Port, fd) 263 | if err != nil { 264 | d.vServer.NextIP() 265 | return nil, err 266 | } 267 | log.Printf("Using Prepared: %s", curIP) 268 | return conn, nil 269 | } 270 | 271 | // v2ray connecting to "domestic" servers, no caching results 272 | log.Printf("Not Using Prepared: %s,%s", network, Address) 273 | resolved, err := d.lookupAddr(Address) 274 | if err != nil { 275 | return nil, err 276 | } 277 | 278 | fd, err := d.getFd(dest.Network) 279 | if err != nil { 280 | return nil, err 281 | } 282 | 283 | // use the first resolved address. 284 | // the result IP may vary, eg: IPv6 addrs comes first if client has ipv6 address 285 | return d.fdConn(ctx, resolved.IPs[0], resolved.Port, fd) 286 | } 287 | 288 | func (d *ProtectedDialer) DestIpAddress() net.IP { 289 | if d == nil || d.vServer == nil { 290 | return nil 291 | } 292 | return d.vServer.currentIP() 293 | } 294 | 295 | func (d *ProtectedDialer) fdConn(ctx context.Context, ip net.IP, port int, fd int) (net.Conn, error) { 296 | 297 | defer unix.Close(fd) 298 | 299 | // call android VPN service to "protect" the fd connecting straight out 300 | if !d.Protect(fd) { 301 | log.Printf("fdConn fail to protect, Close Fd: %d", fd) 302 | return nil, errors.New("fail to protect") 303 | } 304 | 305 | sa := &unix.SockaddrInet6{ 306 | Port: port, 307 | } 308 | copy(sa.Addr[:], ip.To16()) 309 | 310 | if err := unix.Connect(fd, sa); err != nil { 311 | log.Printf("fdConn unix.Connect err, Close Fd: %d Err: %v", fd, err) 312 | return nil, err 313 | } 314 | 315 | file := os.NewFile(uintptr(fd), "Socket") 316 | if file == nil { 317 | // returned value will be nil if fd is not a valid file descriptor 318 | return nil, errors.New("fdConn fd invalid") 319 | } 320 | 321 | defer file.Close() 322 | //Closing conn does not affect file, and closing file does not affect conn. 323 | conn, err := net.FileConn(file) 324 | if err != nil { 325 | log.Printf("fdConn FileConn Close Fd: %d Err: %v", fd, err) 326 | return nil, err 327 | } 328 | 329 | return conn, nil 330 | } 331 | -------------------------------------------------------------------------------- /libv2ray_support_test.go: -------------------------------------------------------------------------------- 1 | package libv2ray 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "fmt" 7 | "net" 8 | "sync" 9 | "testing" 10 | "time" 11 | 12 | v2net "github.com/GFW-knocker/Xray-core/common/net" 13 | ) 14 | 15 | type fakeSupportSet struct{} 16 | 17 | func (f fakeSupportSet) Protect(int) bool { 18 | return true 19 | } 20 | 21 | func TestProtectedDialer_PrepareDomain(t *testing.T) { 22 | type args struct { 23 | domainName string 24 | } 25 | tests := []struct { 26 | name string 27 | args args 28 | }{ 29 | // TODO: Add test cases. 30 | {"", args{"baidu.com:80"}}, 31 | // {"", args{"cloudflare.com:443"}}, 32 | // {"", args{"apple.com:443"}}, 33 | // {"", args{"110.110.110.110:443"}}, 34 | // {"", args{"[2002:1234::1]:443"}}, 35 | } 36 | d := NewPreotectedDialer(fakeSupportSet{}) 37 | for _, tt := range tests { 38 | ch := make(chan struct{}) 39 | t.Run(tt.name, func(t *testing.T) { 40 | go d.PrepareDomain(tt.args.domainName, ch, false) 41 | 42 | time.Sleep(time.Second) 43 | go d.vServer.NextIP() 44 | t.Log(d.vServer.currentIP()) 45 | }) 46 | } 47 | 48 | time.Sleep(time.Second) 49 | } 50 | 51 | func TestProtectedDialer_Dial(t *testing.T) { 52 | 53 | tests := []struct { 54 | name string 55 | wantErr bool 56 | }{ 57 | // TODO: Add test cases. 58 | {"baidu.com:80", false}, 59 | {"cloudflare.com:80", false}, 60 | {"172.16.192.11:80", true}, 61 | // {"172.16.192.10:80", true}, 62 | // {"[2fff:4322::1]:443", true}, 63 | // {"[fc00::1]:443", true}, 64 | } 65 | for _, tt := range tests { 66 | t.Run(tt.name, func(t *testing.T) { 67 | ch := make(chan struct{}) 68 | 69 | d := NewPreotectedDialer(fakeSupportSet{}) 70 | d.currentServer = tt.name 71 | 72 | go d.PrepareDomain(tt.name, ch, false) 73 | 74 | var wg sync.WaitGroup 75 | 76 | dial := func() { 77 | defer wg.Done() 78 | dest, _ := v2net.ParseDestination("tcp:" + tt.name) 79 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 80 | defer cancel() 81 | 82 | conn, err := d.Dial(ctx, nil, dest, nil) 83 | if err != nil { 84 | t.Log(err) 85 | return 86 | } 87 | _host, _, _ := net.SplitHostPort(tt.name) 88 | fmt.Fprintf(conn, fmt.Sprintf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", _host)) 89 | status, err := bufio.NewReader(conn).ReadString('\n') 90 | t.Logf("%#v, %#v\n", status, err) 91 | conn.Close() 92 | } 93 | 94 | for n := 0; n < 3; n++ { 95 | wg.Add(1) 96 | go dial() 97 | // time.Sleep(time.Millisecond * 10) 98 | // d.pendingMap[tt.name] = make(chan struct{}) 99 | } 100 | 101 | wg.Wait() 102 | }) 103 | } 104 | } 105 | 106 | func Test_resolved_NextIP(t *testing.T) { 107 | type fields struct { 108 | domain string 109 | IPs []net.IP 110 | Port int 111 | } 112 | tests := []struct { 113 | name string 114 | fields fields 115 | }{ 116 | // TODO: Add test cases. 117 | {"test1", 118 | fields{ 119 | domain: "www.baidu.com", 120 | IPs: []net.IP{ 121 | net.ParseIP("1.2.3.4"), 122 | net.ParseIP("4.3.2.1"), 123 | net.ParseIP("1234::1"), 124 | net.ParseIP("4321::1"), 125 | }, 126 | }}, 127 | } 128 | for _, tt := range tests { 129 | t.Run(tt.name, func(t *testing.T) { 130 | r := &resolved{ 131 | domain: tt.fields.domain, 132 | IPs: tt.fields.IPs, 133 | Port: tt.fields.Port, 134 | } 135 | t.Logf("%v", r.IPs) 136 | t.Logf("%v", r.currentIP()) 137 | r.NextIP() 138 | t.Logf("%v", r.currentIP()) 139 | r.NextIP() 140 | t.Logf("%v", r.currentIP()) 141 | r.NextIP() 142 | t.Logf("%v", r.currentIP()) 143 | time.Sleep(3 * time.Second) 144 | r.NextIP() 145 | t.Logf("%v", r.currentIP()) 146 | time.Sleep(5 * time.Second) 147 | r.NextIP() 148 | t.Logf("%v", r.currentIP()) 149 | }) 150 | } 151 | } 152 | --------------------------------------------------------------------------------