├── .github ├── FUNDING.yml └── workflows │ ├── codeql-analysis.yml │ ├── release.yml │ └── test.yml ├── LICENSE ├── README.md ├── README_zh_cn.md ├── cmd └── v2gen │ └── main.go ├── common ├── base64 │ └── base64.go ├── mean │ ├── mean.go │ └── mean_test.go └── split │ └── split.go ├── config.go ├── go.mod ├── go.sum ├── infra ├── v2genconf.go └── v2rayconf.go ├── link.go ├── ping └── ping.go └── vmess ├── miniv2ray.go ├── miniv2ray_distro.go ├── vmess.go └── vmessping.go /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: ioc # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | name: "CodeQL" 7 | 8 | on: 9 | push: 10 | branches: [master] 11 | pull_request: 12 | # The branches below must be a subset of the branches above 13 | branches: [master] 14 | schedule: 15 | - cron: '0 3 * * 3' 16 | 17 | jobs: 18 | analyze: 19 | name: Analyze 20 | runs-on: ubuntu-latest 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | # Override automatic language detection by changing the below list 26 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 27 | language: ['go'] 28 | # Learn more... 29 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 30 | 31 | steps: 32 | - name: Checkout repository 33 | uses: actions/checkout@v2 34 | 35 | # Initializes the CodeQL tools for scanning. 36 | - name: Initialize CodeQL 37 | uses: github/codeql-action/init@v1 38 | with: 39 | languages: ${{ matrix.language }} 40 | # If you wish to specify custom queries, you can do so here or in a config file. 41 | # By default, queries listed here will override any specified in a config file. 42 | # Prefix the list here with "+" to use these queries and those in the config file. 43 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 44 | 45 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 46 | # If this step fails, then you should remove it and run the build manually (see below) 47 | - name: Autobuild 48 | uses: github/codeql-action/autobuild@v1 49 | 50 | # ℹ️ Command-line programs to run using the OS shell. 51 | # 📚 https://git.io/JvXDl 52 | 53 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 54 | # and modify them (or add more) to build your code if your project 55 | # uses a compiled language 56 | 57 | #- run: | 58 | # make bootstrap 59 | # make release 60 | 61 | - name: Perform CodeQL Analysis 62 | uses: github/codeql-action/analyze@v1 63 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: 5 | - 'v*' 6 | 7 | jobs: 8 | Build-and-Release: 9 | name: build and release 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: checkout 13 | uses: actions/checkout@v1 14 | 15 | - name: set up Go 1.15 16 | uses: actions/setup-go@v1 17 | with: 18 | go-version: 1.15 19 | id: go 20 | 21 | - name: set variables 22 | run: | 23 | mkdir build 24 | echo "VERSION=${GITHUB_REF##*/}" >> $GITHUB_ENV 25 | echo "GOPRIVATE=github.com/v2ray/v2ray-core" >> $GITHUB_ENV 26 | shell: bash 27 | env: 28 | GITHUB_REF: ${{ github.ref }} 29 | 30 | - name: build v2gen 31 | run: | 32 | LDFLAGS="-s -w -X main.Version=${VERSION}" 33 | CMDPATH=./cmd/v2gen/ 34 | GOOS=linux GOARCH=amd64 go build -ldflags="${LDFLAGS}" -gcflags=-trimpath=${GOPATH} -asmflags=-trimpath=${GOPATH} -v -o ./build/v2gen_amd64_linux ${CMDPATH} 35 | GOOS=linux GOARCH=arm64 go build -ldflags="${LDFLAGS}" -gcflags=-trimpath=${GOPATH} -asmflags=-trimpath=${GOPATH} -v -o ./build/v2gen_arm64_linux ${CMDPATH} 36 | GOOS=linux GOARCH=386 go build -ldflags="${LDFLAGS}" -gcflags=-trimpath=${GOPATH} -asmflags=-trimpath=${GOPATH} -v -o ./build/v2gen_386_linux ${CMDPATH} 37 | GOOS=linux GOARCH=arm go build -ldflags="${LDFLAGS}" -gcflags=-trimpath=${GOPATH} -asmflags=-trimpath=${GOPATH} -v -o ./build/v2gen_arm_linux ${CMDPATH} 38 | GOOS=windows GOARCH=amd64 go build -ldflags="${LDFLAGS}" -gcflags=-trimpath=${GOPATH} -asmflags=-trimpath=${GOPATH} -v -o ./build/v2gen_amd64_windows.exe ${CMDPATH} 39 | GOOS=windows GOARCH=386 go build -ldflags="${LDFLAGS}" -gcflags=-trimpath=${GOPATH} -asmflags=-trimpath=${GOPATH} -v -o ./build/v2gen_386_windows.exe ${CMDPATH} 40 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="${LDFLAGS} -extldflags \"-static\"" -gcflags=-trimpath=${GOPATH} -asmflags=-trimpath=${GOPATH} -v -o ./build/v2gen_amd64_linux_static ${CMDPATH} 41 | 42 | - name: compress 43 | uses: actions-github/upx@master 44 | with: 45 | dir: './build' 46 | upx_args: '-9' 47 | 48 | - name: release 49 | uses: softprops/action-gh-release@v1 50 | if: startsWith(github.ref, 'refs/tags/') 51 | with: 52 | files: | 53 | ./build/v2gen_amd64_linux 54 | ./build/v2gen_arm64_linux 55 | ./build/v2gen_386_linux 56 | ./build/v2gen_arm_linux 57 | ./build/v2gen_amd64_windows.exe 58 | ./build/v2gen_386_windows.exe 59 | ./build/v2gen_amd64_linux_static 60 | ./LICENSE 61 | ./README.md 62 | ./README_zh_cn.md 63 | env: 64 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 65 | 66 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | strategy: 6 | matrix: 7 | go-version: [1.15] 8 | platform: [ubuntu-latest, macos-latest] 9 | 10 | runs-on: ${{ matrix.platform }} 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v2 14 | 15 | - name: Install Go 16 | uses: actions/setup-go@v2 17 | with: 18 | go-version: ${{ matrix.go-version }} 19 | 20 | - name: set variables 21 | run: | 22 | mkdir build 23 | echo "GOPRIVATE=github.com/v2ray/v2ray-core" >> $GITHUB_ENV 24 | 25 | - name: Test 26 | run: go test ./... -v -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2020 Richard Chen(i@iochen.com) 4 | 5 | Copyright (c) 2019-2020 V2Fly(hi@v2fly.com) (Code in vmess/{miniv2ray*,vmessping}.go files) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # v2gen 2 | 3 | ```diff 4 | - The project will no longer be supported, please use other alternatives instead. 5 | - 该项目不再受支持,建议使用其它替代品 6 | ``` 7 | 8 | A powerful V2Ray config generator 9 | 10 | You can use use vmess ping instead of ICMP ping 11 | 12 | [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?)](https://pkg.go.dev/iochen.com/v2gen) 13 | ![GitHub top language](https://img.shields.io/github/languages/top/iochen/v2gen) 14 | ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/iochen/v2gen) 15 | ![Go](https://github.com/iochen/v2gen/workflows/Test/badge.svg) 16 | 17 | 18 | [简体中文](README_zh_cn.md) 19 | 20 | ## Preview 21 | ``` 22 | [ 0] Server A [451ms (0 errors)] 23 | [ 1] Server B [452ms (0 errors)] 24 | [ 3] Server C [251ms (0 errors)] 25 | ... 26 | [25] Server Z [652ms (2 errors)] 27 | ===================== 28 | Please Select: 29 | ``` 30 | 31 | ## Build or Download 32 | ```sh 33 | git clone https://github.com/iochen/v2gen/ && cd v2gen 34 | env GOPRIVATE=github.com/v2ray/v2ray-core go build ./cmd/v2gen 35 | ``` 36 | or Download in GitHub Releases 37 | 38 | ## Quick start 39 | ```sh 40 | v2gen -u {{Your subscription link}} -o {{Your V2Ray config path}} 41 | ``` 42 | 43 | ## Param 44 | 45 | ```Param 46 | Usage of ./v2gen: 47 | -best 48 | use best node judged by ping result 49 | -c int 50 | ping count for each node (default 3) 51 | -config string 52 | v2gen config path (default "/etc/v2ray/v2gen.ini") 53 | -dst string 54 | test destination url (vmess ping only) (default "https://cloudflare.com/cdn-cgi/trace") 55 | -init 56 | init v2gen config (specify certain path with -config) 57 | -log string 58 | log output file (default "-") 59 | -loglevel string 60 | log level (default "warn") 61 | -o string 62 | output path (default "/etc/v2ray/config.json") 63 | -ping 64 | ping nodes (default true) 65 | -pipe 66 | read from pipe (default true) 67 | -random 68 | random node index 69 | -template string 70 | V2Ray template path 71 | -thread int 72 | threads used when pinging (default 3) 73 | -u string 74 | subscription address(URL) 75 | -v show version 76 | ``` 77 | 78 | ## V2Gen user config 79 | 80 | You can use `v2gen --init` to generate one 81 | 82 | ```yaml 83 | # V2Ray log level 84 | # ( debug | info | warning | error | none ) 85 | loglevel: warning 86 | 87 | # Socks port 88 | socksPort: 1080 89 | 90 | # Http port 91 | httpPort: 1081 92 | 93 | # If allow UDP traffic 94 | # ( true | false ) 95 | udp: true 96 | 97 | # Security 98 | # ( aes-128-gcm | aes-256-gcm | chacha20-poly1305 | auto | none ) 99 | security: aes-256-gcm 100 | 101 | # If enable mux 102 | # ( true | false ) 103 | mux: true 104 | 105 | # Mux concurrency num 106 | concurrency: 8 107 | 108 | # DNS server 109 | dns1: https://1.1.1.1/dns-query 110 | dns2: https://dns.quad9.net/dns-query 111 | 112 | # If China sites and ips directly connect 113 | # ( true | false ) 114 | china: true 115 | 116 | ``` 117 | 118 | The following config may NOT work on every node 119 | 120 | ```yaml 121 | # If allow insecure connection ( true | false ) 122 | allowInsecure: false 123 | 124 | # KCP mtu num 125 | mtu: 1350 126 | 127 | # KCP tti num 128 | tti: 20 129 | 130 | # KCP max upload speed 131 | # Unit: MB/s 132 | up: 5 133 | 134 | # KCP max download speed 135 | # Unit: MB/s 136 | down: 20 137 | 138 | # If enable UDP congestion control ( true | false ) 139 | congestion: false 140 | 141 | # Read buffer size 142 | # Unit: MB 143 | readBufferSize: 1 144 | 145 | # Write buffer size 146 | # Unit: MB 147 | writeBufferSize: 1 148 | ``` 149 | 150 | ## LINCENSE 151 | 152 | MIT LICENSE 153 | -------------------------------------------------------------------------------- /README_zh_cn.md: -------------------------------------------------------------------------------- 1 | # v2gen 2 | 3 | v2gen 是一个强大的 V2Ray 订阅客户端,使用 vmessping 代替 ICMP ping 4 | 5 | [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?)](https://pkg.go.dev/iochen.com/v2gen) 6 | ![GitHub top language](https://img.shields.io/github/languages/top/iochen/v2gen) 7 | ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/iochen/v2gen) 8 | ![Go](https://github.com/iochen/v2gen/workflows/Test/badge.svg) 9 | 10 | [English](README.md) 11 | 12 | ## 预览 13 | ``` 14 | [ 0] Server A [451ms (0 errors)] 15 | [ 1] Server B [452ms (0 errors)] 16 | [ 3] Server C [251ms (0 errors)] 17 | ... 18 | [25] Server Z [652ms (2 errors)] 19 | ===================== 20 | Please Select: 21 | ``` 22 | 23 | 24 | 25 | ## 编译或下载 26 | 27 | ```sh 28 | git clone https://github.com/iochen/v2gen/ && cd v2gen 29 | env GOPRIVATE=github.com/v2ray/v2ray-core go build ./cmd/v2gen 30 | ``` 31 | 或在 GitHub Release 上下载 32 | 33 | 34 | 35 | ## 快速开始 36 | 37 | ```sh 38 | v2gen -u {{Your subscription link}} -o {{Your V2Ray config path}} 39 | ``` 40 | 41 | 42 | 43 | ## 参数 44 | 45 | ```Param 46 | Usage of v2gen: 47 | -best 48 | 根据 ping 的结果选择最优节点 49 | -c int 50 | 每个节点 ping 的次数 (default 3) 51 | -config string 52 | v2gen 配置文件路径 (default "/etc/v2ray/v2gen.ini") 53 | -dst string 54 | 测试目标地址 (vmess ping only) (default "https://cloudflare.com/cdn-cgi/trace") 55 | -init 56 | 初始化 v2gen 配置 (specify certain path with -config) 57 | -log string 58 | 日志输出文件 (default "-") 59 | -loglevel string 60 | 日志等级 (default "warn") 61 | -o string 62 | 输出路径 (default "/etc/v2ray/config.json") 63 | -ping 64 | ping 节点 (default true) 65 | -pipe 66 | 自动从 pipe 中读取 (default true) 67 | -random 68 | 随机节点 69 | -template string 70 | V2Ray 模板路径 71 | -thread int 72 | ping 线程数 (default 3) 73 | -u string 74 | 订阅链接 (URL) 75 | -v 展示版本 76 | ``` 77 | 78 | 79 | 80 | ## v2gen 用户配置 81 | 82 | 你可以使用 `v2gen --init` 来生成初始化配置文件 83 | 84 | ```yaml 85 | # V2Ray log level 86 | # ( debug | info | warning | error | none ) 87 | loglevel: warning 88 | 89 | # Socks port 90 | socksPort: 1080 91 | 92 | # Http port 93 | httpPort: 1081 94 | 95 | # If allow UDP traffic 96 | # ( true | false ) 97 | udp: true 98 | 99 | # Security 100 | # ( aes-128-gcm | aes-256-gcm | chacha20-poly1305 | auto | none ) 101 | security: aes-256-gcm 102 | 103 | # If enable mux 104 | # ( true | false ) 105 | mux: true 106 | 107 | # Mux concurrency num 108 | concurrency: 8 109 | 110 | # DNS server 111 | dns1: https://1.1.1.1/dns-query 112 | dns2: https://dns.quad9.net/dns-query 113 | 114 | # If China sites and ips directly connect 115 | # ( true | false ) 116 | china: true 117 | 118 | ``` 119 | 120 | 以下配置可能不对每个节点都有效 121 | 122 | ```yaml 123 | # If allow insecure connection ( true | false ) 124 | allowInsecure: false 125 | 126 | # KCP mtu num 127 | mtu: 1350 128 | 129 | # KCP tti num 130 | tti: 20 131 | 132 | # KCP max upload speed 133 | # Unit: MB/s 134 | up: 5 135 | 136 | # KCP max download speed 137 | # Unit: MB/s 138 | down: 20 139 | 140 | # If enable UDP congestion control ( true | false ) 141 | congestion: false 142 | 143 | # Read buffer size 144 | # Unit: MB 145 | readBufferSize: 1 146 | 147 | # Write buffer size 148 | # Unit: MB 149 | writeBufferSize: 1 150 | ``` 151 | 152 | 153 | 154 | ## 模板文件 155 | 156 | ### 渲染流程 157 | 158 | 1. 将所有配置(节点信息,用户配置,默认配置 (排名分先后))转化为 `key-value` 对(哈系表) 159 | 2. 读取用户模板或使用默认模板,并加载 160 | 3. 将加载好的模板文件中 `{{foobar}}` 部分替换为 1 中哈系表中 `foobar` 所对的键值([关键词会进行特殊处理](#关键词)) 161 | 4. 输出 162 | 163 | ### 关键词 164 | 165 | #### china 166 | 167 | 如果值为 `true`,则将模板文件中 `{{china_ip}}` 与 `{{china_sites}}` 替换为 168 | 169 | ``` 170 | "geoip:cn", 171 | ``` 172 | 173 | 和 174 | 175 | ``` 176 | { 177 | "type": "field", 178 | "outboundTag": "direct", 179 | "domain": ["geosite:cn"] 180 | }, 181 | ``` 182 | 183 | 否则,则将配置文件中 `{{china_ip}}` 与 `{{china_sites}}` 均替换为空白 184 | 185 | #### tls 186 | 187 | 如果值为 `tls`,则将模板文件中 `{{tls}}` 替换为 188 | 189 | ``` 190 | { 191 | "serverName": "{{address}}", 192 | "allowInsecure": {{allowInsecure}}, 193 | "alpn": ["http/1.1"] 194 | } 195 | ``` 196 | 197 | 否则则将模板文件中 `{{tls}}` 替换为 `null`, 198 | 199 | #### network 200 | 201 | 值为 `kcp`:将 `{{kcp}}` 替换为 202 | 203 | ``` 204 | { 205 | "mtu": {{mtu}}, 206 | "tti": {{tti}}, 207 | "uplinkCapacity": {{up}}, 208 | "downlinkCapacity": {{down}}, 209 | "congestion": {{congestion}}, 210 | "readBufferSize": {{readBufferSize}}, 211 | "writeBufferSize": {{writeBufferSize}}, 212 | "header": { 213 | "type": "{{type}}" 214 | } 215 | } 216 | ``` 217 | 218 | 值为 `ws`:将 `{{ws}}` 替换为 219 | 220 | ``` 221 | { 222 | "mtu": {{mtu}}, 223 | "tti": {{tti}}, 224 | "uplinkCapacity": {{up}}, 225 | "downlinkCapacity": {{down}}, 226 | "congestion": {{congestion}}, 227 | "readBufferSize": {{readBufferSize}}, 228 | "writeBufferSize": {{writeBufferSize}}, 229 | "header": { 230 | "type": "{{type}}" 231 | } 232 | } 233 | ``` 234 | 235 | 值为 `http`:将`{{http}}` 替换为 236 | 237 | ``` 238 | { 239 | "host": [{{host}}], 240 | "path": "{{path}}" 241 | } 242 | ``` 243 | 244 | 并将哈系表中 `host` 所对值由 `foo,bar,foobar` 修改为 `"foo","bar","foobar"` 245 | 246 | 值为 `quic`:将`{{quic}}` 替换为 247 | 248 | ``` 249 | { 250 | "security": "{{host}}", 251 | "key": "{{path}}", 252 | "header": { 253 | "type": "{{type}}" 254 | } 255 | } 256 | ``` 257 | 258 | 否则将不做修改 259 | 260 | ### 其他 261 | 262 | #### 计划 263 | 264 | 使用 `text/template` 代替现有方案 265 | 266 | #### 默认模板文件 267 | 268 | ```json 269 | { 270 | "log": { 271 | "loglevel": "{{loglevel}}" 272 | }, 273 | "inbounds": [ 274 | { 275 | "port": {{socksPort}}, 276 | "protocol": "socks", 277 | "settings": { 278 | "udp": {{udp}} 279 | } 280 | }, 281 | { 282 | "port": {{httpPort}}, 283 | "protocol": "http", 284 | "settings": { 285 | "udp": {{udp}} 286 | } 287 | } 288 | ], 289 | "outbounds": [ 290 | { 291 | "protocol": "vmess", 292 | "settings": { 293 | "vnext": [ 294 | { 295 | "address": "{{address}}", 296 | "port": {{serverPort}}, 297 | "users": [ 298 | { 299 | "id": "{{uuid}}", 300 | "alterId": {{aid}}, 301 | "security": "{{security}}" 302 | } 303 | ] 304 | } 305 | ] 306 | }, 307 | "streamSettings": { 308 | "network": "{{network}}", 309 | "security": "{{streamSecurity}}", 310 | "tlsSettings": {{tls}}, 311 | "kcpSettings": {{kcp}}, 312 | "wsSettings": {{ws}}, 313 | "httpSettings": {{http}}, 314 | "quicSettings": {{quic}}, 315 | "mux": { 316 | "enabled": {{mux}}, 317 | "concurrency": {{concurrency}} 318 | } 319 | } 320 | }, 321 | { 322 | "protocol": "freedom", 323 | "settings": {}, 324 | "tag": "direct" 325 | } 326 | ], 327 | "dns": { 328 | "servers": [ 329 | "{{dns1}}", 330 | "{{dns2}}", 331 | "localhost" 332 | ] 333 | }, 334 | "routing": { 335 | "strategy": "rules", 336 | "settings": { 337 | "domainStrategy": "IPIfNonMatch", 338 | "rules": [{{china_sites}} 339 | { 340 | "type": "field", 341 | "outboundTag": "direct", 342 | "ip": [{{china_ip}} 343 | "geoip:private" 344 | ] 345 | } 346 | ] 347 | } 348 | } 349 | } 350 | ``` 351 | 352 | ## LICENSE 353 | 354 | MIT LICENSE 355 | -------------------------------------------------------------------------------- /cmd/v2gen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "errors" 6 | "flag" 7 | "fmt" 8 | "go/build" 9 | "io/ioutil" 10 | "log" 11 | "math/big" 12 | "net/http" 13 | "os" 14 | "path/filepath" 15 | "runtime" 16 | "sort" 17 | "strings" 18 | "unicode/utf8" 19 | 20 | "github.com/remeh/sizedwaitgroup" 21 | 22 | "github.com/sirupsen/logrus" 23 | 24 | "iochen.com/v2gen/v2" 25 | "iochen.com/v2gen/v2/common/base64" 26 | "iochen.com/v2gen/v2/common/mean" 27 | "iochen.com/v2gen/v2/infra" 28 | "iochen.com/v2gen/v2/ping" 29 | "iochen.com/v2gen/v2/vmess" 30 | ) 31 | 32 | var ( 33 | Version = "v2.0.0-dev" 34 | 35 | FlagLoglevel = flag.String("loglevel", "warn", "log level") 36 | FlagLog = flag.String("log", "-", "log output file") 37 | FlagAddr = flag.String("u", "", "subscription address(URL)") 38 | FlagOut = flag.String("o", "/etc/v2ray/config.json", "output path") 39 | FlagConf = flag.String("config", "/etc/v2ray/v2gen.ini", "v2gen config path") 40 | FlagTPL = flag.String("template", "", "V2Ray template path") 41 | FlagInit = flag.Bool("init", false, "init v2gen config (specify certain path with -config)") 42 | FlagRandom = flag.Bool("random", false, "random node index") 43 | FlagPing = flag.Bool("ping", true, "ping nodes") 44 | FlagDest = flag.String("dst", "https://cloudflare.com/cdn-cgi/trace", "test destination url (vmess ping only)") 45 | FlagCount = flag.Int("c", 3, "ping count for each node") 46 | // FlagMedian = flag.Bool("med", false, "use median instead of ArithmeticMean") 47 | FlagThreads = flag.Int("thread", 3, "threads used when pinging") 48 | FlagBest = flag.Bool("best", false, "use best node judged by ping result") 49 | FlagPipe = flag.Bool("pipe", true, "read from pipe") 50 | FlagVersion = flag.Bool("v", false, "show version") 51 | ) 52 | 53 | /* 54 | function main may be too long, here is a simple step list: 55 | ################################################################################################# 56 | # STEP 1 (READ): # 57 | # 1. read links from subscription(net) and pipe. # 58 | # # 59 | # STEP 2 (PROCESS): # 60 | # TYPE 1 (PING): # 61 | # SUBTYPE 1.1 (BEST): # 62 | # 1. ping. # 63 | # 2. choose the best node. # 64 | # # 65 | # SUBTYPE 1.2 (RANDOM): # 66 | # 1. ping. # 67 | # 2. filter out available node list A. # 68 | # NOTE: if exist nodes that no error, then A would be them(it), # 69 | # else A would be all of them. # 70 | # 3. randomly choose one from A. # 71 | # # 72 | # SUBTYPE 1.3 (DEFAULT): # 73 | # 1. ping. # 74 | # 2. print nodes and ping info. # 75 | # 3. wait for user's choosing. # 76 | # # 77 | # TYPE 2 (NOT PING): # 78 | # SUBTYPE 1.2 (RANDOM): # 79 | # 1. randomly choose one from nodes. # 80 | # # 81 | # SUBTYPE 1.3 (DEFAULT): # 82 | # 1. print nodes and ping info. # 83 | # 2. wait for user's choosing. # 84 | # # 85 | # STEP 3 (RENDER AND WRITE): # 86 | # 1. render and write. # 87 | ################################################################################################# 88 | */ 89 | 90 | type PingInfo struct { 91 | Status *ping.Status 92 | Duration ping.Duration 93 | Link v2gen.Link 94 | Err error 95 | } 96 | 97 | type PingInfoList []*PingInfo 98 | 99 | func (pf *PingInfoList) Len() int { 100 | return len(*pf) 101 | } 102 | 103 | func (pf *PingInfoList) Less(i, j int) bool { 104 | if (*pf)[i].Err != nil { 105 | return false 106 | } else if (*pf)[j].Err != nil { 107 | return true 108 | } 109 | 110 | if len((*pf)[i].Status.Errors) != len((*pf)[j].Status.Errors) { 111 | return len((*pf)[i].Status.Errors) < len((*pf)[j].Status.Errors) 112 | } 113 | 114 | return (*pf)[i].Duration < (*pf)[j].Duration 115 | } 116 | 117 | func (pf *PingInfoList) Swap(i, j int) { 118 | (*pf)[i], (*pf)[j] = (*pf)[j], (*pf)[i] 119 | } 120 | 121 | func main() { 122 | flag.Parse() 123 | 124 | /* 125 | LOG PART 126 | */ 127 | logger := logrus.New() 128 | if *FlagLog != "-" && *FlagLog != "" { 129 | file, err := os.Create(*FlagLog) 130 | if err != nil { 131 | logrus.Fatal(err) 132 | } 133 | defer file.Close() 134 | _, err = file.Write([]byte(version() + "\n")) 135 | if err != nil { 136 | panic("cannot write into log file") 137 | } 138 | logger.Out = file 139 | } 140 | 141 | // set log level 142 | level, err := logrus.ParseLevel(*FlagLoglevel) 143 | if err != nil { 144 | logger.Panic(err) 145 | } 146 | logger.SetLevel(level) 147 | 148 | /* 149 | FLAG PART 150 | */ 151 | // if -v || trace, debug, info 152 | if *FlagVersion { 153 | fmt.Println(version()) 154 | return 155 | } else if level > logrus.ErrorLevel { 156 | fmt.Println(version()) 157 | } 158 | 159 | // if -init 160 | if *FlagInit { 161 | err := ioutil.WriteFile(*FlagConf, []byte(infra.DefaultV2GenConf), 0644) 162 | if err != nil { 163 | panic(err) 164 | return 165 | } 166 | logger.Info("v2gen config initialized") 167 | return 168 | } 169 | 170 | /* 171 | LINK PART 172 | */ 173 | var linkList []v2gen.Link // combine links from different sources 174 | // read from subscribe address(net) 175 | if *FlagAddr != "" { 176 | logger.Infof("Reading from %s...", *FlagAddr) 177 | resp, err := http.Get(*FlagAddr) 178 | if err != nil { 179 | logger.Fatal(err) 180 | } 181 | defer resp.Body.Close() 182 | bytes, err := ioutil.ReadAll(resp.Body) 183 | if err != nil { 184 | logger.Fatal(err) 185 | } 186 | links, err := ParseLinks(bytes) 187 | if err != nil { 188 | logger.Fatal(err) 189 | } 190 | linkList = append(linkList, links...) 191 | } 192 | 193 | // check whether reading from pipe 194 | if fi, _ := os.Stdin.Stat(); (fi.Mode()&os.ModeCharDevice) == 0 && *FlagPipe { 195 | logger.Info("Reading from pipe...") 196 | bytes, err := ioutil.ReadAll(os.Stdin) 197 | if err != nil { 198 | log.Fatal(err) 199 | } 200 | links, err := ParseLinks(bytes) 201 | if err != nil { 202 | logger.Fatal(err) 203 | } 204 | linkList = append(linkList, links...) 205 | 206 | } 207 | 208 | // if no Link, then exit 209 | if len(linkList) == 0 { 210 | logger.Warn("no available links, nothing to do") 211 | os.Exit(0) 212 | } 213 | 214 | var chosenLink v2gen.Link 215 | var spaceCount = func(i int, str string) string { 216 | rl := utf8.RuneCountInString(str) 217 | c := i - (len(str)+rl)/2 218 | if c < 0 { 219 | c = 0 220 | } 221 | return strings.Repeat(" ", c) 222 | } 223 | if *FlagPing { // if ping 224 | // make ping info list 225 | pingInfoList := make(PingInfoList, len(linkList)) 226 | wg := sizedwaitgroup.New(*FlagThreads) 227 | for i := range linkList { 228 | wg.Add() 229 | go func(i int) { 230 | logger.Debugf("[%d/%d]Pinging %s\n", i, len(linkList)-1, linkList[i].Safe()) 231 | if level > logrus.ErrorLevel { 232 | fmt.Printf("\rPinging %d/%d", i, len(linkList)-1) 233 | } 234 | defer func() { 235 | wg.Done() 236 | }() 237 | pingInfoList[i] = &PingInfo{ 238 | Link: linkList[i], 239 | } 240 | status, err := linkList[i].Ping(*FlagCount, *FlagDest) 241 | if status.Durations == nil || len(*status.Durations) == 0 { 242 | pingInfoList[i].Err = errors.New("all error") 243 | status.Durations = &ping.DurationList{-1} 244 | } 245 | if err != nil { 246 | pingInfoList[i].Err = err 247 | pingInfoList[i].Status = &ping.Status{ 248 | Durations: &ping.DurationList{}, 249 | } 250 | } else { 251 | pingInfoList[i].Status = &status 252 | } 253 | }(i) 254 | } 255 | wg.Wait() 256 | fmt.Println() 257 | 258 | for i := range pingInfoList { 259 | var ok bool 260 | pingInfoList[i].Duration, ok = mean.ArithmeticMean(pingInfoList[i].Status.Durations).(ping.Duration) 261 | if !ok { 262 | pingInfoList[i].Duration = 0 263 | } 264 | } 265 | sort.Sort(&pingInfoList) 266 | if *FlagBest { // if ping && best 267 | chosenLink = pingInfoList[0].Link 268 | } else if *FlagRandom { // if ping && rand 269 | pingInfoList = AvailableLinks(pingInfoList) 270 | i, err := Random(len(pingInfoList)) 271 | if err != nil { 272 | logger.Fatal(err) 273 | } 274 | chosenLink = pingInfoList[i].Link 275 | } else { // if ping && not rand && not best 276 | for i := range pingInfoList { 277 | fmt.Printf("[%2d] %s%s[%-7s(%d errors)]\n", 278 | i, pingInfoList[i].Link.Description(), 279 | spaceCount(30, pingInfoList[i].Link.Description()), 280 | pingInfoList[i].Duration.Precision(1e6), len(pingInfoList[i].Status.Errors)) 281 | } 282 | i := Select(len(pingInfoList)) 283 | chosenLink = pingInfoList[i].Link 284 | } 285 | } else { // if not ping 286 | if *FlagRandom { // if not ping && rand 287 | i, err := Random(len(linkList)) 288 | if err != nil { 289 | logger.Fatal(err) 290 | } 291 | chosenLink = linkList[i] 292 | } else { // if not ping && not rand 293 | for i := range linkList { 294 | fmt.Printf("[%2d] %s%s\n", 295 | i, linkList[i].Description(), 296 | spaceCount(30, linkList[i].Description())) 297 | } 298 | i := Select(len(linkList)) 299 | chosenLink = linkList[i] 300 | } 301 | } 302 | 303 | /* 304 | CONFIG PART 305 | */ 306 | 307 | var template []byte 308 | template = []byte(infra.ConfigTpl) 309 | if *FlagTPL != "" { 310 | tpl, err := ioutil.ReadFile(*FlagTPL) 311 | if err != nil { 312 | logrus.Error(err, "using default template...") 313 | } else { 314 | template = tpl 315 | } 316 | } 317 | 318 | v2genConf := infra.V2genConfig{} 319 | confFile, err := ioutil.ReadFile(*FlagConf) 320 | if err == nil { 321 | v2genConf = infra.ParseV2genConf(confFile) 322 | } 323 | conf := infra.DefaultConf() 324 | bytes, err := infra.GenV2RayConf(*conf.Append(v2genConf).Append(chosenLink.Config()), template) 325 | if err != nil { 326 | logrus.Fatal(err) 327 | } 328 | 329 | if *FlagOut == "-" || *FlagOut == "" { 330 | fmt.Println(string(bytes)) 331 | return 332 | } else { 333 | err := ioutil.WriteFile(*FlagOut, bytes, 0644) 334 | if err != nil { 335 | logrus.Fatal(err) 336 | } else { 337 | if level > logrus.ErrorLevel { 338 | fmt.Printf("config has been written to %s\n", filepath.Clean(*FlagOut)) 339 | } 340 | } 341 | } 342 | } 343 | 344 | func version() string { 345 | return fmt.Sprintf("v2gen %s, V2Ray %s (%s %dcores %s/%s)", Version, vmess.CoreVersion(), 346 | runtime.Version(), runtime.NumCPU(), build.Default.GOOS, build.Default.GOARCH) 347 | } 348 | 349 | func ParseLinks(b []byte) ([]v2gen.Link, error) { 350 | s, err := base64.Decode(string(b)) 351 | if err != nil { 352 | return nil, err 353 | } 354 | linkList, err := vmess.Parse(s) 355 | if err != nil { 356 | return nil, err 357 | } 358 | links := make([]v2gen.Link, len(linkList)) 359 | for i := range linkList { 360 | links[i] = linkList[i] 361 | } 362 | return links, err 363 | } 364 | 365 | func AvailableLinks(pil PingInfoList) PingInfoList { 366 | var pingInfoList PingInfoList 367 | for i := range pil { 368 | if pil[i].Err != nil && len(pil[i].Status.Errors) == 0 { 369 | pingInfoList = append(pingInfoList, pil[i]) 370 | } 371 | } 372 | 373 | if len(pingInfoList) != 0 { 374 | return pingInfoList 375 | } else { 376 | return pil 377 | } 378 | } 379 | 380 | // Select returns an int [0,max) 381 | func Select(max int) int { 382 | var in int 383 | fmt.Print("=====================\nPlease Select: ") 384 | _, err := fmt.Scanf("%d", &in) 385 | if err != nil || in < 0 || in >= max { 386 | fmt.Println("wrong number, please reselect") 387 | return Select(max) 388 | } 389 | return in 390 | } 391 | 392 | func Random(max int) (int, error) { 393 | n, err := rand.Int(rand.Reader, big.NewInt(int64(max))) 394 | if err != nil { 395 | return 0, err 396 | } 397 | 398 | return int(n.Int64()), nil 399 | } 400 | -------------------------------------------------------------------------------- /common/base64/base64.go: -------------------------------------------------------------------------------- 1 | package base64 2 | 3 | import ( 4 | "encoding/base64" 5 | "errors" 6 | ) 7 | 8 | func Decode(str string) (string, error) { 9 | de, err := base64.StdEncoding.DecodeString(str) 10 | if err == nil { 11 | return string(de), err 12 | } 13 | 14 | de, err = base64.RawStdEncoding.DecodeString(str) 15 | if err == nil { 16 | return string(de), err 17 | } 18 | 19 | de, err = base64.URLEncoding.DecodeString(str) 20 | if err == nil { 21 | return string(de), err 22 | } 23 | 24 | de, err = base64.RawURLEncoding.DecodeString(str) 25 | if err == nil { 26 | return string(de), err 27 | } 28 | 29 | return "", errors.New("no proper base64 decode method for: " + str) 30 | } 31 | 32 | func Encode(str string) string { 33 | return base64.StdEncoding.EncodeToString([]byte(str)) 34 | } 35 | -------------------------------------------------------------------------------- /common/mean/mean.go: -------------------------------------------------------------------------------- 1 | package mean 2 | 3 | import ( 4 | "sort" 5 | ) 6 | 7 | type Value interface { 8 | Sum(value Value) Value 9 | DividedBy(i int) Value 10 | } 11 | 12 | type Mean interface { 13 | Value(index int) Value 14 | sort.Interface 15 | } 16 | 17 | func Median(m Mean) Value { 18 | l := m.Len() 19 | sort.Sort(m) 20 | if l%2 == 0 { 21 | return m.Value(l / 2).Sum(m.Value(l/2 - 1)).DividedBy(2) 22 | } else { 23 | return m.Value(l/2 - 1) 24 | } 25 | } 26 | 27 | func ArithmeticMean(m Mean) Value { 28 | l := m.Len() 29 | var sum Value 30 | 31 | if l == 0 { 32 | return nil 33 | } 34 | 35 | for i := 0; i < l; i++ { 36 | if i == 0 { 37 | sum = m.Value(i) 38 | } 39 | sum = m.Value(i).Sum(sum) 40 | } 41 | return sum.DividedBy(l) 42 | } 43 | -------------------------------------------------------------------------------- /common/mean/mean_test.go: -------------------------------------------------------------------------------- 1 | package mean_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "iochen.com/v2gen/v2/common/mean" 7 | ) 8 | 9 | type T int 10 | 11 | type S struct { 12 | v []T 13 | } 14 | 15 | func (v T) Sum(value mean.Value) mean.Value { 16 | return v + value.(T) 17 | } 18 | 19 | func (v T) DividedBy(i int) mean.Value { 20 | return v / T(i) 21 | } 22 | 23 | func (s *S) Value(index int) mean.Value { 24 | return s.v[index] 25 | } 26 | 27 | func (s *S) Len() int { 28 | return len(s.v) 29 | } 30 | 31 | func (s *S) Less(i, j int) bool { 32 | return s.v[i] < s.v[j] 33 | } 34 | 35 | func (s *S) Swap(i, j int) { 36 | s.v[i], s.v[j] = s.v[j], s.v[i] 37 | } 38 | 39 | func TestArithmeticMean(t *testing.T) { 40 | s := &S{v: []T{ 41 | 65, 75, 30, 10, 22, 42 | 77, 13, 99, 60, 45, 43 | 46, 40, 28, 34, 44, 44 | 63, 90, 81, 74, 85, 45 | 11, 93, 80, 41, 2, 46 | 80, 20, 90, 71, 30, 47 | 58, 84, 83, 79, 5, 48 | 98, 69, 71, 21, 45, 49 | 80, 61, 41, 75, 27, 50 | 98, 21, 63, 71, 92, 51 | }} 52 | 53 | t.Log(mean.Median(s)) 54 | t.Log(mean.ArithmeticMean(s)) 55 | } 56 | -------------------------------------------------------------------------------- /common/split/split.go: -------------------------------------------------------------------------------- 1 | package split 2 | 3 | import "strings" 4 | 5 | var sep = map[rune]bool{ 6 | ' ': true, 7 | '\n': true, 8 | ',': true, 9 | ';': true, 10 | '\t': true, 11 | '\f': true, 12 | '\v': true, 13 | '\r': true, 14 | } 15 | 16 | func Split(s string) []string { 17 | return strings.FieldsFunc(s, func(r rune) bool { 18 | return sep[r] 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | package v2gen 2 | 3 | type Config interface { 4 | Config() map[string]string 5 | } 6 | 7 | //// Render renders configs with template 8 | //// important config should reorder after 9 | //func Render(tpl string, configs ...Config) (io.Reader, error) { 10 | // config := make(map[string]string) 11 | // for i := range configs { 12 | // for k, v := range configs[i].Config() { 13 | // config[k] = v 14 | // } 15 | // } 16 | // 17 | // parse, err := template.New("default").Parse(tpl) 18 | // if err != nil { 19 | // return nil, err 20 | // } 21 | // buf := new(bytes.Buffer) 22 | // err = parse.Execute(buf, config) 23 | // if err != nil { 24 | // return nil, err 25 | // } 26 | // return buf, nil 27 | //} 28 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module iochen.com/v2gen/v2 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/remeh/sizedwaitgroup v1.0.0 7 | github.com/sirupsen/logrus v1.6.0 8 | v2ray.com/core v4.19.1+incompatible 9 | ) 10 | 11 | replace v2ray.com/core => github.com/v2ray/v2ray-core v4.31.0+incompatible 12 | -------------------------------------------------------------------------------- /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/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= 12 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 13 | github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= 14 | github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= 15 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 16 | github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= 17 | github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= 18 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 19 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 20 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 21 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 22 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 23 | github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 24 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 25 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 26 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 27 | github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165 h1:BS21ZUJ/B5X2UVUbczfmdWH7GapPWAhxcMsDnjJTU1E= 28 | github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= 29 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 30 | github.com/ebfe/bcrypt_pbkdf v0.0.0-20140212075826-3c8d2dcb253a/go.mod h1:/CZpbhAusDOobpcb9yubw46kdYjq0zRC0Wpg9a9zFQM= 31 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 32 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 33 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 34 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 35 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 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/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 39 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 40 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 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/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 44 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 45 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 46 | github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 47 | github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= 48 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 49 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 50 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 51 | github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= 52 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 53 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 54 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 55 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 56 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 57 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 58 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 59 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 60 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 61 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 62 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 63 | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= 64 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 65 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 66 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 67 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 68 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 69 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 70 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 71 | github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= 72 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 73 | github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= 74 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 75 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 76 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 77 | github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= 78 | github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= 79 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 80 | github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= 81 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 82 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 83 | github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= 84 | github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c= 85 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 86 | github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= 87 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 88 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 89 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 90 | github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= 91 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 92 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 93 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 94 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 95 | github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 96 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 97 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 98 | github.com/lucas-clemente/quic-go v0.18.1 h1:DMR7guC0NtVS8zNZR3IO7NARZvZygkSC56GGtC6cyys= 99 | github.com/lucas-clemente/quic-go v0.18.1/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg= 100 | github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= 101 | github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 102 | github.com/marten-seemann/qpack v0.2.0/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= 103 | github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc= 104 | github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= 105 | github.com/marten-seemann/qtls-go1-15 v0.1.0 h1:i/YPXVxz8q9umso/5y474CNcHmTpA+5DH+mFPjx6PZg= 106 | github.com/marten-seemann/qtls-go1-15 v0.1.0/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= 107 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 108 | github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= 109 | github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo= 110 | github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= 111 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 112 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 113 | github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= 114 | github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= 115 | github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= 116 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 117 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 118 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 119 | github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= 120 | github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= 121 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 122 | github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= 123 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 124 | github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= 125 | github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= 126 | github.com/pires/go-proxyproto v0.2.0 h1:WyYKlv9pkt77b+LjMvPfwrsAxviaGCFhG4KDIy1ofLY= 127 | github.com/pires/go-proxyproto v0.2.0/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY= 128 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 129 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 130 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 131 | github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 132 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 133 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 134 | github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 135 | github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 136 | github.com/remeh/sizedwaitgroup v1.0.0 h1:VNGGFwNo/R5+MJBf6yrsr110p0m4/OX4S3DCy7Kyl5E= 137 | github.com/remeh/sizedwaitgroup v1.0.0/go.mod h1:3j2R4OIe/SeS6YDhICBy22RWjJC5eNCJ1V+9+NVNYlo= 138 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 139 | github.com/seiflotfy/cuckoofilter v0.0.0-20200511222245-56093a4d3841 h1:pnfutQFsV7ySmHUeX6ANGfPsBo29RctUvDn8G3rmJVw= 140 | github.com/seiflotfy/cuckoofilter v0.0.0-20200511222245-56093a4d3841/go.mod h1:ET5mVvNjwaGXRgZxO9UZr7X+8eAf87AfIYNwRSp9s4Y= 141 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 142 | github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= 143 | github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= 144 | github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= 145 | github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= 146 | github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= 147 | github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= 148 | github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= 149 | github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= 150 | github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= 151 | github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= 152 | github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= 153 | github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= 154 | github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= 155 | github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= 156 | github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= 157 | github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= 158 | github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= 159 | github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= 160 | github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= 161 | github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 162 | github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= 163 | github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= 164 | github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= 165 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 166 | github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= 167 | github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= 168 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 169 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 170 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 171 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 172 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 173 | github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= 174 | github.com/v2ray/v2ray-core v4.31.0+incompatible h1:y16XMLmeUErQ3yqZBrlRFQx9CYYxYCrw3zDqGbyWYvI= 175 | github.com/v2ray/v2ray-core v4.31.0+incompatible/go.mod h1:/On+ZM+St3UF1qnuLEkywkVc5tV7W31d06hbbjpkD9E= 176 | github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= 177 | github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= 178 | github.com/xiaokangwang/VSign v0.0.0-20200828155424-dc1c86b73fbf/go.mod h1:jTwBnzBuqZP3VX/Z65ErYb9zd4anQprSC7N38TmAp1E= 179 | github.com/xtls/go v0.0.0-20201007031018-d42c13c57942 h1:J+h5T5dtgVtszPN0vOFBzTapdVRZ16sXP6o6RPFNs20= 180 | github.com/xtls/go v0.0.0-20201007031018-d42c13c57942/go.mod h1:5TB2+k58gx4A4g2Nf5miSHNDF6CuAzHKpWBooLAshTs= 181 | go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= 182 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 183 | go.starlark.net v0.0.0-20201006213952-227f4aabceb5 h1:ApvY/1gw+Yiqb/FKeks3KnVPWpkR3xzij82XPKLjJVw= 184 | go.starlark.net v0.0.0-20201006213952-227f4aabceb5/go.mod h1:f0znQkUKRrkk36XxWbGjMqQM8wGv/xHBVE2qc3B5oFU= 185 | go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= 186 | golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= 187 | golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 188 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 189 | golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 190 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 191 | golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 192 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 193 | golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE= 194 | golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 195 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 196 | golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 197 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 198 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 199 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 200 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 201 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 202 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 203 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 204 | golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 205 | golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 206 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 207 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 208 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 209 | golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 210 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 211 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 212 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 213 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 214 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 215 | golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M= 216 | golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 217 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 218 | golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 219 | golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 220 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 221 | golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 222 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 223 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 224 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 225 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 226 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 227 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 228 | golang.org/x/sync v0.0.0-20200930132711-30421366ff76 h1:JnxiSYT3Nm0BT2a8CyvYyM6cnrWpidecD1UuSYbhKm0= 229 | golang.org/x/sync v0.0.0-20200930132711-30421366ff76/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 230 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 231 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 232 | golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 233 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 234 | golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 235 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 236 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 237 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 238 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 239 | golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 240 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 241 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 242 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 243 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 244 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 245 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 246 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 247 | golang.org/x/sys v0.0.0-20201006155630-ac719f4daadf h1:Bg47KQy0JhTHuf4sLiQwTMKwUMfSDwgSGatrxGR7nLM= 248 | golang.org/x/sys v0.0.0-20201006155630-ac719f4daadf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 249 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 250 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 251 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 252 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 253 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 254 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 255 | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 256 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 257 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 258 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 259 | golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 260 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 261 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 262 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 263 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 264 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 265 | golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 266 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 267 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 268 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 269 | google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= 270 | google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= 271 | google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= 272 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 273 | google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 274 | google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 275 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 276 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 277 | google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 278 | google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 279 | google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= 280 | google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 281 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 282 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 283 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= 284 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 285 | google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 286 | google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= 287 | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 288 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 289 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 290 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 291 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 292 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 293 | google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= 294 | google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 295 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 296 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 297 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 298 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 299 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 300 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 301 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 302 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 303 | google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= 304 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 305 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 306 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 307 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 308 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 309 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 310 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 311 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 312 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 313 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 314 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 315 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 316 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 317 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 318 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 319 | grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= 320 | h12.io/socks v1.0.1/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck= 321 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 322 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 323 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 324 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 325 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 326 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 327 | sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= 328 | sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= 329 | -------------------------------------------------------------------------------- /infra/v2genconf.go: -------------------------------------------------------------------------------- 1 | package infra 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | type V2genConfig map[string]string 8 | 9 | func (config *V2genConfig) Config() map[string]string { 10 | return map[string]string(*config) 11 | } 12 | 13 | func (config *V2genConfig) Append(conf map[string]string) *V2genConfig { 14 | for k, v := range conf { 15 | (*config)[k] = v 16 | } 17 | return config 18 | } 19 | 20 | // ParseV2genConf parses the file into a [string]string map 21 | func ParseV2genConf(b []byte) V2genConfig { 22 | V2GenSettings := make(map[string]string) 23 | 24 | // file to lines 25 | lines := strings.Split(string(b), "\n") 26 | for _, s := range lines { 27 | s = strings.TrimLeft(s, " ") 28 | s = strings.TrimLeft(s, "\t") 29 | if s == "" { 30 | continue 31 | } 32 | if s[0] == '#' { 33 | continue 34 | } 35 | 36 | // Split "k v" to {k,v} 37 | line := strings.FieldsFunc(s, func(r rune) bool { 38 | if r == ' ' || r == '\t' { 39 | return true 40 | } 41 | return false 42 | }) 43 | 44 | // check if is {k,v} 45 | if len(line) != 2 { 46 | continue 47 | } 48 | 49 | line[0] = strings.TrimRight(line[0], ":") 50 | 51 | V2GenSettings[line[0]] = line[1] // Set Settings[k]=v 52 | } 53 | 54 | return V2genConfig(V2GenSettings) 55 | } 56 | 57 | const DefaultV2GenConf = ` 58 | ##################### 59 | # v2gen user config # 60 | ##################### 61 | 62 | # V2Ray log level 63 | # ( debug | info | warning | error | none ) 64 | loglevel: warning 65 | 66 | # Socks port 67 | socksPort: 1080 68 | 69 | # Http port 70 | httpPort: 1081 71 | 72 | # If allow UDP traffic 73 | # ( true | false ) 74 | udp: true 75 | 76 | # Security 77 | # ( aes-128-gcm | aes-256-gcm | chacha20-poly1305 | auto | none ) 78 | security: aes-256-gcm 79 | 80 | # If enable mux 81 | # ( true | false ) 82 | mux: true 83 | 84 | # Mux concurrency num 85 | concurrency: 8 86 | 87 | # DNS server 88 | dns1: https://1.1.1.1/dns-query 89 | dns2: https://dns.quad9.net/dns-query 90 | 91 | # If China sites and ips directly connect 92 | # ( true | false ) 93 | china: true 94 | 95 | 96 | #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 97 | #@ NOTICE: @ 98 | #@ The following settings may NOT work on every node @ 99 | #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 100 | 101 | # If allow insecure connection ( true | false ) 102 | allowInsecure: false 103 | 104 | # KCP mtu num 105 | mtu: 1350 106 | 107 | # KCP tti num 108 | tti: 20 109 | 110 | # KCP max upload speed 111 | # Unit: MB/s 112 | up: 5 113 | 114 | # KCP max download speed 115 | # Unit: MB/s 116 | down: 20 117 | 118 | # If enable UDP congestion control ( true | false ) 119 | congestion: false 120 | 121 | # Read buffer size 122 | # Unit: MB 123 | readBufferSize: 1 124 | 125 | # Write buffer size 126 | # Unit: MB 127 | writeBufferSize: 1 128 | 129 | ############################## 130 | # Thank you for using v2gen! # 131 | ############################## 132 | ` 133 | -------------------------------------------------------------------------------- /infra/v2rayconf.go: -------------------------------------------------------------------------------- 1 | package infra 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "strings" 8 | ) 9 | 10 | func parseHost(s string) string { 11 | parts := strings.Split(s, ",") 12 | for i := range parts { 13 | parts[i] = "\"" + parts[i] + "\"" 14 | } 15 | s = strings.Join(parts, ",") 16 | return s 17 | } 18 | 19 | func prettyPrint(b []byte) ([]byte, error) { 20 | var out bytes.Buffer 21 | err := json.Indent(&out, b, "", "\t") 22 | return out.Bytes(), err 23 | } 24 | 25 | func DefaultConf() V2genConfig { 26 | Settings := make(V2genConfig) 27 | 28 | //default settings 29 | Settings["loglevel"] = "warning" 30 | Settings["socksPort"] = "1080" 31 | Settings["udp"] = "true" 32 | Settings["httpPort"] = "1081" 33 | Settings["security"] = "aes-256-gcm" 34 | Settings["mux"] = "true" 35 | Settings["concurrency"] = "8" 36 | Settings["dns1"] = "https://1.1.1.1/dns-query" 37 | Settings["dns2"] = "https://dns.quad9.net/dns-query" 38 | Settings["china"] = "true" 39 | Settings["tls"] = "null" 40 | Settings["kcp"] = "null" 41 | Settings["ws"] = "null" 42 | Settings["quic"] = "null" 43 | Settings["http"] = "null" 44 | Settings["allowInsecure"] = "false" 45 | Settings["mtu"] = "1350" 46 | Settings["tti"] = "20" 47 | Settings["up"] = "5" 48 | Settings["down"] = "20" 49 | Settings["congestion"] = "false" 50 | Settings["readBufferSize"] = "1" 51 | Settings["writeBufferSize"] = "1" 52 | 53 | return Settings 54 | } 55 | 56 | func GenV2RayConf(conf V2genConfig, template ...[]byte) ([]byte, error) { 57 | v2rayConf := ConfigTpl 58 | if len(template) > 0 { 59 | if len(template) != 1 { 60 | return nil, errors.New("too many templates") 61 | } 62 | v2rayConf = string(template[0]) 63 | } 64 | 65 | if conf["china"] == "true" { 66 | v2rayConf = strings.ReplaceAll(v2rayConf, "{{china_ip}}", "\n"+`"geoip:cn",`) 67 | v2rayConf = strings.ReplaceAll(v2rayConf, "{{china_sites}}", ChinaSites) 68 | } else { 69 | v2rayConf = strings.ReplaceAll(v2rayConf, "{{china_ip}}", "") 70 | v2rayConf = strings.ReplaceAll(v2rayConf, "{{china_sites}}", "") 71 | } 72 | 73 | // set stream 74 | if conf["tls"] == "tls" { 75 | v2rayConf = strings.ReplaceAll(v2rayConf, "{{tls}}", TLSObject) 76 | } else { 77 | v2rayConf = strings.ReplaceAll(v2rayConf, "{{tls}}", "null") 78 | } 79 | 80 | switch conf["network"] { 81 | case "kcp": 82 | v2rayConf = strings.ReplaceAll(v2rayConf, "{{kcp}}", KcpObject) 83 | case "ws": 84 | v2rayConf = strings.ReplaceAll(v2rayConf, "{{ws}}", WsObject) 85 | case "http": 86 | v2rayConf = strings.ReplaceAll(v2rayConf, "{{http}}", HttpObject) 87 | conf["host"] = parseHost(conf["host"]) 88 | case "quic": 89 | v2rayConf = strings.ReplaceAll(v2rayConf, "{{quic}}", QuicObject) 90 | } 91 | 92 | // set other settings 93 | for k, v := range conf { 94 | v2rayConf = strings.ReplaceAll(v2rayConf, "{{"+k+"}}", v) 95 | } 96 | 97 | return prettyPrint([]byte(v2rayConf)) 98 | } 99 | 100 | const ConfigTpl = `{ 101 | "log": { 102 | "loglevel": "{{loglevel}}" 103 | }, 104 | "inbounds": [ 105 | { 106 | "port": {{socksPort}}, 107 | "protocol": "socks", 108 | "settings": { 109 | "udp": {{udp}} 110 | } 111 | }, 112 | { 113 | "port": {{httpPort}}, 114 | "protocol": "http", 115 | "settings": { 116 | "udp": {{udp}} 117 | } 118 | } 119 | ], 120 | "outbounds": [ 121 | { 122 | "protocol": "vmess", 123 | "settings": { 124 | "vnext": [ 125 | { 126 | "address": "{{address}}", 127 | "port": {{serverPort}}, 128 | "users": [ 129 | { 130 | "id": "{{uuid}}", 131 | "alterId": {{aid}}, 132 | "security": "{{security}}" 133 | } 134 | ] 135 | } 136 | ] 137 | }, 138 | "streamSettings": { 139 | "network": "{{network}}", 140 | "security": "{{streamSecurity}}", 141 | "tlsSettings": {{tls}}, 142 | "kcpSettings": {{kcp}}, 143 | "wsSettings": {{ws}}, 144 | "httpSettings": {{http}}, 145 | "quicSettings": {{quic}}, 146 | "mux": { 147 | "enabled": {{mux}}, 148 | "concurrency": {{concurrency}} 149 | } 150 | } 151 | }, 152 | { 153 | "protocol": "freedom", 154 | "settings": {}, 155 | "tag": "direct" 156 | } 157 | ], 158 | "dns": { 159 | "servers": [ 160 | "{{dns1}}", 161 | "{{dns2}}", 162 | "localhost" 163 | ] 164 | }, 165 | "routing": { 166 | "strategy": "rules", 167 | "settings": { 168 | "domainStrategy": "IPIfNonMatch", 169 | "rules": [{{china_sites}} 170 | { 171 | "type": "field", 172 | "outboundTag": "direct", 173 | "ip": [{{china_ip}} 174 | "geoip:private" 175 | ] 176 | } 177 | ] 178 | } 179 | } 180 | }` 181 | 182 | const ChinaSites = ` 183 | { 184 | "type": "field", 185 | "outboundTag": "direct", 186 | "domain": ["geosite:cn"] 187 | },` 188 | 189 | const ( 190 | TLSObject = `{ 191 | "serverName": "{{address}}", 192 | "allowInsecure": {{allowInsecure}}, 193 | "alpn": ["http/1.1"] 194 | }` 195 | 196 | WsObject = `{ 197 | "path": "{{path}}", 198 | "headers": { 199 | "Host": "{{host}}" 200 | } 201 | }` 202 | 203 | KcpObject = ` 204 | { 205 | "mtu": {{mtu}}, 206 | "tti": {{tti}}, 207 | "uplinkCapacity": {{up}}, 208 | "downlinkCapacity": {{down}}, 209 | "congestion": {{congestion}}, 210 | "readBufferSize": {{readBufferSize}}, 211 | "writeBufferSize": {{writeBufferSize}}, 212 | "header": { 213 | "type": "{{type}}" 214 | } 215 | }` 216 | 217 | HttpObject = `{ 218 | "host": [{{host}}], 219 | "path": "{{path}}" 220 | }` 221 | QuicObject = `{ 222 | "security": "{{host}}", 223 | "key": "{{path}}", 224 | "header": { 225 | "type": "{{type}}" 226 | } 227 | }` 228 | ) 229 | -------------------------------------------------------------------------------- /link.go: -------------------------------------------------------------------------------- 1 | package v2gen 2 | 3 | import ( 4 | "fmt" 5 | 6 | "iochen.com/v2gen/v2/ping" 7 | ) 8 | 9 | type Link interface { 10 | fmt.Stringer 11 | Ping(round int, dst string) (ping.Status, error) 12 | DestAddr() string 13 | Description() string 14 | Safe() string 15 | Config 16 | } 17 | -------------------------------------------------------------------------------- /ping/ping.go: -------------------------------------------------------------------------------- 1 | package ping 2 | 3 | import ( 4 | "time" 5 | 6 | "iochen.com/v2gen/v2/common/mean" 7 | ) 8 | 9 | type Duration time.Duration 10 | 11 | func (d Duration) Precision(i int64) Duration { 12 | p := Duration(i) 13 | return (d / p) * p 14 | } 15 | 16 | func (d Duration) String() string { 17 | return time.Duration(d).String() 18 | } 19 | 20 | func (d Duration) Sum(value mean.Value) mean.Value { 21 | return d + value.(Duration) 22 | } 23 | 24 | func (d Duration) DividedBy(i int) mean.Value { 25 | return d / Duration(i) 26 | } 27 | 28 | type DurationList []Duration 29 | 30 | func (dList *DurationList) Value(index int) mean.Value { 31 | return (*dList)[index] 32 | } 33 | 34 | func (dList *DurationList) Len() int { 35 | return len(*dList) 36 | } 37 | 38 | func (dList *DurationList) Less(i, j int) bool { 39 | return (*dList)[i] < (*dList)[j] 40 | } 41 | 42 | func (dList *DurationList) Swap(i, j int) { 43 | (*dList)[i], (*dList)[j] = (*dList)[j], (*dList)[i] 44 | } 45 | 46 | type Status struct { 47 | Durations *DurationList 48 | Errors []*error 49 | } 50 | 51 | func (ps *Status) Value(index int) mean.Value { 52 | return (*ps.Durations)[index] 53 | } 54 | -------------------------------------------------------------------------------- /vmess/miniv2ray.go: -------------------------------------------------------------------------------- 1 | package vmess 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "io/ioutil" 9 | "net" 10 | "net/http" 11 | "strings" 12 | "time" 13 | 14 | "v2ray.com/core" 15 | "v2ray.com/core/app/dispatcher" 16 | applog "v2ray.com/core/app/log" 17 | "v2ray.com/core/app/proxyman" 18 | commlog "v2ray.com/core/common/log" 19 | v2net "v2ray.com/core/common/net" 20 | "v2ray.com/core/common/serial" 21 | "v2ray.com/core/infra/conf" 22 | ) 23 | 24 | func Vmess2Outbound(v *Link, usemux bool) (*core.OutboundHandlerConfig, error) { 25 | out := &conf.OutboundDetourConfig{} 26 | out.Tag = "proxy" 27 | out.Protocol = "vmess" 28 | out.MuxSettings = &conf.MuxConfig{} 29 | if usemux { 30 | out.MuxSettings.Enabled = true 31 | out.MuxSettings.Concurrency = 8 32 | } 33 | 34 | p := conf.TransportProtocol(v.Net) 35 | s := &conf.StreamConfig{ 36 | Network: &p, 37 | Security: v.TLS, 38 | } 39 | 40 | switch v.Net { 41 | case "tcp": 42 | s.TCPSettings = &conf.TCPConfig{} 43 | if v.Type == "" || v.Type == "none" { 44 | s.TCPSettings.HeaderConfig = json.RawMessage([]byte(`{ "type": "none" }`)) 45 | } else { 46 | pathb, _ := json.Marshal(strings.Split(v.Path, ",")) 47 | hostb, _ := json.Marshal(strings.Split(v.Host, ",")) 48 | s.TCPSettings.HeaderConfig = json.RawMessage([]byte(fmt.Sprintf(` 49 | { 50 | "type": "http", 51 | "request": { 52 | "path": %s, 53 | "headers": { 54 | "Host": %s 55 | } 56 | } 57 | } 58 | `, string(pathb), string(hostb)))) 59 | } 60 | case "kcp": 61 | s.KCPSettings = &conf.KCPConfig{} 62 | s.KCPSettings.HeaderConfig = json.RawMessage([]byte(fmt.Sprintf(`{ "type": "%s" }`, v.Type))) 63 | case "ws": 64 | s.WSSettings = &conf.WebSocketConfig{} 65 | s.WSSettings.Path = v.Path 66 | s.WSSettings.Headers = map[string]string{ 67 | "Host": v.Host, 68 | } 69 | case "h2", "http": 70 | s.HTTPSettings = &conf.HTTPConfig{ 71 | Path: v.Path, 72 | } 73 | if v.Host != "" { 74 | h := conf.StringList(strings.Split(v.Host, ",")) 75 | s.HTTPSettings.Host = &h 76 | } 77 | } 78 | 79 | if v.TLS == "tls" { 80 | s.TLSSettings = &conf.TLSConfig{ 81 | Insecure: true, 82 | } 83 | if v.Host != "" { 84 | s.TLSSettings.ServerName = v.Host 85 | } 86 | } 87 | 88 | out.StreamSetting = s 89 | oset := json.RawMessage([]byte(fmt.Sprintf(`{ 90 | "vnext": [ 91 | { 92 | "address": "%s", 93 | "port": %v, 94 | "users": [ 95 | { 96 | "id": "%s", 97 | "alterId": %v, 98 | "security": "auto" 99 | } 100 | ] 101 | } 102 | ] 103 | }`, v.Add, v.Port, v.ID, v.Aid))) 104 | out.Settings = &oset 105 | return out.Build() 106 | } 107 | 108 | func startV2Ray(lk *Link, verbose, usemux bool) (*core.Instance, error) { 109 | loglevel := commlog.Severity_Error 110 | if verbose { 111 | loglevel = commlog.Severity_Debug 112 | } 113 | 114 | ob, err := Vmess2Outbound(lk, usemux) 115 | if err != nil { 116 | return nil, err 117 | } 118 | config := &core.Config{ 119 | App: []*serial.TypedMessage{ 120 | serial.ToTypedMessage(&applog.Config{ 121 | ErrorLogType: applog.LogType_Console, 122 | ErrorLogLevel: loglevel, 123 | }), 124 | serial.ToTypedMessage(&dispatcher.Config{}), 125 | serial.ToTypedMessage(&proxyman.InboundConfig{}), 126 | serial.ToTypedMessage(&proxyman.OutboundConfig{}), 127 | }, 128 | } 129 | 130 | // commlog.RegisterHandler(commlog.NewLogger(commlog.CreateStderrLogWriter())) 131 | config.Outbound = []*core.OutboundHandlerConfig{ob} 132 | server, err := core.New(config) 133 | if err != nil { 134 | return nil, err 135 | } 136 | 137 | return server, nil 138 | } 139 | 140 | func measureDelay(inst *core.Instance, timeout time.Duration, dest string) (time.Duration, error) { 141 | start := time.Now() 142 | code, _, err := CoreHTTPRequest(inst, timeout, "GET", dest) 143 | if err != nil { 144 | return -1, err 145 | } 146 | if code > 399 { 147 | return -1, fmt.Errorf("status incorrect (>= 400): %d", code) 148 | } 149 | return time.Since(start), nil 150 | } 151 | 152 | func CoreHTTPClient(inst *core.Instance, timeout time.Duration) (*http.Client, error) { 153 | 154 | if inst == nil { 155 | return nil, errors.New("core instance nil") 156 | } 157 | 158 | tr := &http.Transport{ 159 | DisableKeepAlives: true, 160 | DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { 161 | dest, err := v2net.ParseDestination(fmt.Sprintf("%s:%s", network, addr)) 162 | if err != nil { 163 | return nil, err 164 | } 165 | return core.Dial(ctx, inst, dest) 166 | }, 167 | } 168 | 169 | c := &http.Client{ 170 | Transport: tr, 171 | Timeout: timeout, 172 | } 173 | 174 | return c, nil 175 | } 176 | 177 | func CoreHTTPRequest(inst *core.Instance, timeout time.Duration, method, dest string) (int, []byte, error) { 178 | 179 | c, err := CoreHTTPClient(inst, timeout) 180 | if err != nil { 181 | return 0, nil, err 182 | } 183 | 184 | req, _ := http.NewRequest(method, dest, nil) 185 | resp, err := c.Do(req) 186 | if err != nil { 187 | return -1, nil, err 188 | } 189 | defer resp.Body.Close() 190 | 191 | b, _ := ioutil.ReadAll(resp.Body) 192 | return resp.StatusCode, b, nil 193 | } 194 | 195 | func CoreVersion() string { 196 | return core.Version() 197 | } 198 | -------------------------------------------------------------------------------- /vmess/miniv2ray_distro.go: -------------------------------------------------------------------------------- 1 | package vmess 2 | 3 | import ( 4 | // The following are necessary as they register handlers in their init functions. 5 | // Required features. Can't remove unless there is replacements. 6 | _ "v2ray.com/core/app/dispatcher" 7 | _ "v2ray.com/core/app/proxyman/inbound" 8 | _ "v2ray.com/core/app/proxyman/outbound" 9 | 10 | // Default commander and all its services. This is an optional feature. 11 | // _ "v2ray.com/core/app/commander" 12 | // _ "v2ray.com/core/app/log/command" 13 | 14 | // _ "v2ray.com/core/app/proxyman/command" 15 | // _ "v2ray.com/core/app/stats/command" 16 | 17 | // Other optional features. 18 | // _ "v2ray.com/core/app/dns" 19 | // _ "v2ray.com/core/app/log" 20 | // _ "v2ray.com/core/app/policy" 21 | 22 | // _ "v2ray.com/core/app/reverse" 23 | // _ "v2ray.com/core/app/router" 24 | // _ "v2ray.com/core/app/stats" 25 | 26 | // Inbound and outbound proxies. 27 | // _ "v2ray.com/core/proxy/blackhole" 28 | // _ "v2ray.com/core/proxy/dns" 29 | // _ "v2ray.com/core/proxy/dokodemo" 30 | // _ "v2ray.com/core/proxy/freedom" 31 | // _ "v2ray.com/core/proxy/http" 32 | 33 | // _ "v2ray.com/core/proxy/mtproto" 34 | // _ "v2ray.com/core/proxy/shadowsocks" 35 | // _ "v2ray.com/core/proxy/socks" 36 | // _ "v2ray.com/core/proxy/vmess/inbound" 37 | _ "v2ray.com/core/proxy/vmess/outbound" 38 | 39 | // Transports 40 | // _ "v2ray.com/core/transport/internet/domainsocket" 41 | _ "v2ray.com/core/transport/internet/http" 42 | _ "v2ray.com/core/transport/internet/kcp" 43 | _ "v2ray.com/core/transport/internet/quic" 44 | _ "v2ray.com/core/transport/internet/tcp" 45 | _ "v2ray.com/core/transport/internet/tls" 46 | _ "v2ray.com/core/transport/internet/udp" 47 | _ "v2ray.com/core/transport/internet/websocket" 48 | 49 | // Transport headers 50 | _ "v2ray.com/core/transport/internet/headers/http" 51 | _ "v2ray.com/core/transport/internet/headers/noop" 52 | _ "v2ray.com/core/transport/internet/headers/srtp" 53 | _ "v2ray.com/core/transport/internet/headers/tls" 54 | _ "v2ray.com/core/transport/internet/headers/utp" 55 | _ "v2ray.com/core/transport/internet/headers/wechat" 56 | _ "v2ray.com/core/transport/internet/headers/wireguard" 57 | ) 58 | -------------------------------------------------------------------------------- /vmess/vmess.go: -------------------------------------------------------------------------------- 1 | package vmess 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "unicode" 8 | 9 | "iochen.com/v2gen/v2/common/base64" 10 | "iochen.com/v2gen/v2/common/split" 11 | ) 12 | 13 | type Link struct { 14 | Ps string `json:"ps"` 15 | Add string `json:"add"` 16 | Port interface{} `json:"port"` 17 | ID string `json:"id"` 18 | Aid interface{} `json:"aid"` 19 | Net string `json:"net"` 20 | Type string `json:"type"` 21 | Host string `json:"host"` 22 | Path string `json:"path"` 23 | TLS string `json:"tls"` 24 | } 25 | 26 | var ( 27 | ErrWrongProtocol = errors.New("wrong protocol") 28 | ) 29 | 30 | // ParseSingle parses single vmess URL into Link structure 31 | func ParseSingle(vmessURL string) (*Link, error) { 32 | if len(vmessURL) < 8 { 33 | return &Link{}, errors.New(fmt.Sprint("wrong url:", vmessURL)) 34 | } 35 | if vmessURL[:8] != "vmess://" { 36 | return &Link{}, ErrWrongProtocol 37 | } 38 | 39 | j, err := base64.Decode(vmessURL[8:]) 40 | if err != nil { 41 | return &Link{}, err 42 | } 43 | 44 | lk := &Link{} 45 | err = json.Unmarshal([]byte(j), lk) 46 | return lk, err 47 | } 48 | 49 | func Parse(s string) ([]*Link, error) { 50 | var vl []*Link 51 | urlList := split.Split(s) 52 | for i := 0; i < len(urlList); i++ { 53 | lk, err := ParseSingle(urlList[i]) 54 | if err != nil { 55 | if err == ErrWrongProtocol { 56 | continue 57 | } else { 58 | return nil, err 59 | } 60 | } 61 | vl = append(vl, lk) 62 | } 63 | return vl, nil 64 | } 65 | 66 | func (lk *Link) Config() map[string]string { 67 | var config = make(map[string]string) 68 | // set node settings 69 | config["address"] = lk.Add 70 | config["serverPort"] = fmt.Sprintf("%v", lk.Port) 71 | config["uuid"] = lk.ID 72 | config["aid"] = fmt.Sprintf("%v", lk.Aid) 73 | config["streamSecurity"] = lk.TLS 74 | config["network"] = lk.Net 75 | config["tls"] = lk.TLS 76 | config["type"] = lk.Type 77 | config["host"] = lk.Host 78 | config["type"] = lk.Type 79 | config["path"] = lk.Path 80 | return config 81 | } 82 | 83 | // String converts vmess link to vmess:// URL 84 | func (lk *Link) String() string { 85 | b, _ := json.Marshal(lk) 86 | return "vmess://" + base64.Encode(string(b)) 87 | } 88 | 89 | func redact(str string) string { 90 | var result []rune 91 | for _, v := range str { 92 | if unicode.IsDigit(v) { 93 | result = append(result, '0') 94 | continue 95 | } 96 | 97 | if unicode.IsUpper(v) { 98 | result = append(result, 'X') 99 | continue 100 | } 101 | 102 | if unicode.IsLower(v) { 103 | result = append(result, 'x') 104 | continue 105 | } 106 | 107 | result = append(result, v) 108 | } 109 | return string(result) 110 | } 111 | 112 | func (lk *Link) Safe() string { 113 | safeLink := &Link{ 114 | Ps: lk.Ps, 115 | Add: redact(lk.Add), 116 | Port: lk.Port, 117 | ID: redact(lk.ID), 118 | Aid: lk.Aid, 119 | Net: lk.Net, 120 | Type: lk.Type, 121 | Host: redact(lk.Host), 122 | Path: redact(lk.Path), 123 | TLS: lk.TLS, 124 | } 125 | b, _ := json.Marshal(safeLink) 126 | return string(b) 127 | } 128 | 129 | func (lk *Link) DestAddr() string { 130 | return lk.Add 131 | } 132 | 133 | func (lk *Link) Description() string { 134 | return lk.Ps 135 | } 136 | -------------------------------------------------------------------------------- /vmess/vmessping.go: -------------------------------------------------------------------------------- 1 | package vmess 2 | 3 | import ( 4 | "log" 5 | "time" 6 | 7 | "iochen.com/v2gen/v2/ping" 8 | ) 9 | 10 | func (lk *Link) Ping(round int, dst string) (ping.Status, error) { 11 | server, err := startV2Ray(lk, false, false) 12 | if err != nil { 13 | return ping.Status{}, err 14 | } 15 | 16 | defer func() { 17 | if err := server.Close(); err != nil { 18 | log.Println(err) 19 | } 20 | }() 21 | 22 | ps := ping.Status{ 23 | Durations: &ping.DurationList{}, 24 | } 25 | 26 | timeout := make(chan bool, 1) 27 | 28 | go func() { 29 | time.Sleep(3 * time.Duration(round) * time.Second) 30 | timeout <- true 31 | }() 32 | 33 | L: 34 | for count := 0; count < round; count++ { 35 | chDelay := make(chan time.Duration) 36 | go func() { 37 | delay, err := measureDelay(server, 3*time.Second, dst) 38 | if err != nil { 39 | ps.Errors = append(ps.Errors, &err) 40 | } 41 | chDelay <- delay 42 | }() 43 | 44 | select { 45 | case delay := <-chDelay: 46 | if delay > 0 { 47 | *ps.Durations = append(*ps.Durations, ping.Duration(delay)) 48 | } 49 | case <-timeout: 50 | break L 51 | } 52 | } 53 | return ps, nil 54 | } 55 | --------------------------------------------------------------------------------