Full Code of Yuzuki616/V2bX for AI

master b54e9041dfab cached
110 files
315.0 KB
125.3k tokens
353 symbols
1 requests
Download .txt
Showing preview only (343K chars total). Download the full file or copy to clipboard to get everything.
Repository: Yuzuki616/V2bX
Branch: master
Commit: b54e9041dfab
Files: 110
Total size: 315.0 KB

Directory structure:
gitextract_ebfl6u56/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug-report.md
│   │   └── feature-request.md
│   ├── build/
│   │   └── friendly-filenames.json
│   ├── dependabot.yml
│   └── workflows/
│       ├── codeql-analysis.yml
│       └── release.yml
├── .gitignore
├── LICENSE
├── README.md
├── api/
│   ├── iprecoder/
│   │   ├── interface.go
│   │   ├── recorder.go
│   │   ├── redis.go
│   │   └── redis_test.go
│   └── panel/
│       ├── node.go
│       ├── node_test.go
│       ├── panel.go
│       ├── user.go
│       └── utils.go
├── cmd/
│   ├── action_linux.go
│   ├── cmd.go
│   ├── common.go
│   ├── common_test.go
│   ├── install_linux.go
│   ├── server.go
│   ├── server_test.go
│   ├── synctime.go
│   ├── version.go
│   ├── x25519.go
│   └── x25519_test.go
├── common/
│   ├── crypt/
│   │   ├── aes.go
│   │   └── x25519.go
│   ├── exec/
│   │   └── exec.go
│   ├── file/
│   │   └── file.go
│   ├── format/
│   │   └── user.go
│   ├── rate/
│   │   └── rate.go
│   ├── systime/
│   │   ├── time_stub.go
│   │   ├── time_unix.go
│   │   └── time_windows.go
│   └── task/
│       ├── task.go
│       └── task_test.go
├── conf/
│   ├── conf.go
│   ├── conf_test.go
│   ├── core.go
│   ├── log.go
│   ├── node.go
│   ├── old.go
│   └── watch.go
├── core/
│   ├── core.go
│   ├── hy/
│   │   ├── config.go
│   │   ├── counter.go
│   │   ├── counter_test.go
│   │   ├── hy.go
│   │   ├── ipmasker.go
│   │   ├── kploader.go
│   │   ├── mmdb.go
│   │   ├── node.go
│   │   ├── resolver.go
│   │   ├── server.go
│   │   ├── server_test.go
│   │   └── user.go
│   ├── imports/
│   │   ├── hy.go
│   │   ├── imports.go
│   │   └── xray.go
│   ├── interface.go
│   ├── selector.go
│   └── xray/
│       ├── app/
│       │   ├── app.go
│       │   └── dispatcher/
│       │       ├── config.pb.go
│       │       ├── config.proto
│       │       ├── default.go
│       │       ├── dispatcher.go
│       │       ├── errors.generated.go
│       │       ├── fakednssniffer.go
│       │       ├── sniffer.go
│       │       ├── stats.go
│       │       └── stats_test.go
│       ├── distro/
│       │   └── all/
│       │       └── all.go
│       ├── inbound.go
│       ├── node.go
│       ├── outbound.go
│       ├── ss.go
│       ├── trojan.go
│       ├── user.go
│       ├── vmess.go
│       └── xray.go
├── example/
│   ├── config.yml.example
│   ├── custom_inbound.json
│   ├── custom_outbound.json
│   ├── dns.json
│   ├── route.json
│   └── rulelist
├── go.mod
├── go.sum
├── limiter/
│   ├── clear.go
│   ├── conn.go
│   ├── conn_test.go
│   ├── dynamic.go
│   ├── limiter.go
│   └── rule.go
├── main.go
├── node/
│   ├── cert.go
│   ├── controller.go
│   ├── lego/
│   │   ├── cert.go
│   │   ├── lego.go
│   │   ├── lego_test.go
│   │   └── user.go
│   ├── node.go
│   ├── task.go
│   └── user.go
└── test_data/
    ├── 1.key
    └── 1.pem

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE/bug-report.md
================================================
---
name: "Bug 反馈"
about: 创建一个报告以帮助我们修复并改进XrayR
title: ''
labels: awaiting reply, bug
assignees: ''
---

**描述该错误**
简单地描述一下这个bug是什么

**复现**
复现该bug的步骤

**环境和版本**
 - 系统 [例如:Debian 11]
 - 架构 [例如:AMD64]
 - 面板 [例如:V2board]
 - 协议 [例如:vmess]
 - 版本 [例如:0.8.2.2]
 - 部署方式 [例如:一键脚本]

**日志和错误**
请使用`xrayr log`查看并添加日志,以帮助解释你的问题

**额外的内容**
在这里添加关于问题的任何其他内容

================================================
FILE: .github/ISSUE_TEMPLATE/feature-request.md
================================================
---
name: "功能建议"
about: 给XrayR提出建议,让我们做得更好
title: ''
labels: awaiting reply, feature-request
assignees: ''
---

**描述您想要的功能**

清晰简洁的功能描述。

**描述您考虑过的替代方案** 

是否有任何替代方案可以解决这个问题? 

**附加上下文** 

在此处添加有关功能请求的任何其他上下文或截图。

================================================
FILE: .github/build/friendly-filenames.json
================================================
{
    "android-arm64": { "friendlyName": "android-arm64-v8a" },
    "darwin-amd64": { "friendlyName": "macos-64" },
    "darwin-arm64": { "friendlyName": "macos-arm64-v8a" },
    "dragonfly-amd64": { "friendlyName": "dragonfly-64" },
    "freebsd-386": { "friendlyName": "freebsd-32" },
    "freebsd-amd64": { "friendlyName": "freebsd-64" },
    "freebsd-arm64": { "friendlyName": "freebsd-arm64-v8a" },
    "freebsd-arm7": { "friendlyName": "freebsd-arm32-v7a" },
    "linux-386": { "friendlyName": "linux-32" },
    "linux-amd64": { "friendlyName": "linux-64" },
    "linux-arm5": { "friendlyName": "linux-arm32-v5" },
    "linux-arm64": { "friendlyName": "linux-arm64-v8a" },
    "linux-arm6": { "friendlyName": "linux-arm32-v6" },
    "linux-arm7": { "friendlyName": "linux-arm32-v7a" },
    "linux-mips64le": { "friendlyName": "linux-mips64le" },
    "linux-mips64": { "friendlyName": "linux-mips64" },
    "linux-mipslesoftfloat": { "friendlyName": "linux-mips32le-softfloat" },
    "linux-mipsle": { "friendlyName": "linux-mips32le" },
    "linux-mipssoftfloat": { "friendlyName": "linux-mips32-softfloat" },
    "linux-mips": { "friendlyName": "linux-mips32" },
    "linux-ppc64le": { "friendlyName": "linux-ppc64le" },
    "linux-ppc64": { "friendlyName": "linux-ppc64" },
    "linux-riscv64": { "friendlyName": "linux-riscv64" },
    "linux-s390x": { "friendlyName": "linux-s390x" },
    "openbsd-386": { "friendlyName": "openbsd-32" },
    "openbsd-amd64": { "friendlyName": "openbsd-64" },
    "openbsd-arm64": { "friendlyName": "openbsd-arm64-v8a" },
    "openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" },
    "windows-386": { "friendlyName": "windows-32" },
    "windows-amd64": { "friendlyName": "windows-64" },
    "windows-arm7": { "friendlyName": "windows-arm32-v7a" }
  }

================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:
  - package-ecosystem: "gomod" # See documentation for possible values
    directory: "/" # Location of package manifests
    schedule:
      interval: "daily"


================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
  push:
    branches: [ master ]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [ master ]
  schedule:
    - cron: '43 22 * * 3'

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        language: [ 'go' ]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
        # Learn more:
        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed

    steps:
    - name: Checkout repository
      uses: actions/checkout@v2

    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v1
      with:
        languages: ${{ matrix.language }}
        # If you wish to specify custom queries, you can do so here or in a config file.
        # By default, queries listed here will override any specified in a config file.
        # Prefix the list here with "+" to use these queries and those in the config file.
        # queries: ./path/to/local/query, your-org/your-repo/queries@main

    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
    # If this step fails, then you should remove it and run the build manually (see below)
    - name: Autobuild
      uses: github/codeql-action/autobuild@v1

    # ℹ️ Command-line programs to run using the OS shell.
    # 📚 https://git.io/JvXDl

    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
    #    and modify them (or add more) to build your code if your project
    #    uses a compiled language

    #- run: |
    #   make bootstrap
    #   make release

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v1


================================================
FILE: .github/workflows/release.yml
================================================
name: Build and Release

on:
  workflow_dispatch:
  push:
    branches:
      - master
    paths:
      - "**/*.go"
      - "go.mod"
      - "go.sum"
      - ".github/workflows/*.yml"
  pull_request:
    types: [opened, synchronize, reopened]
    paths:
      - "**/*.go"
      - "go.mod"
      - "go.sum"
      - ".github/workflows/*.yml"
  release:
    types: [published]

jobs:

  build:
    strategy:
      matrix:
        # Include amd64 on all platforms.
        goos: [windows, freebsd, linux, dragonfly, darwin]
        goarch: [amd64, 386]
        exclude:
          # Exclude i386 on darwin and dragonfly.
          - goarch: 386
            goos: dragonfly
          - goarch: 386
            goos: darwin
        include:
          # BEIGIN MacOS ARM64
          - goos: darwin
            goarch: arm64
          # END MacOS ARM64
          # BEGIN Linux ARM 5 6 7
          - goos: linux
            goarch: arm
            goarm: 7
          - goos: linux
            goarch: arm
            goarm: 6
          - goos: linux
            goarch: arm
            goarm: 5
          # END Linux ARM 5 6 7
          # BEGIN Android ARM 8
          - goos: android
            goarch: arm64
          # END Android ARM 8
          # BEGIN Other architectures
          # BEGIN riscv64 & ARM64
          - goos: linux
            goarch: arm64
          - goos: linux
            goarch: riscv64
          # END riscv64 & ARM64
          # BEGIN MIPS
          - goos: linux
            goarch: mips64
          - goos: linux
            goarch: mips64le
          - goos: linux
            goarch: mipsle
          - goos: linux
            goarch: mips
          # END MIPS
          # BEGIN PPC
          - goos: linux
            goarch: ppc64
          - goos: linux
            goarch: ppc64le
          # END PPC
          # BEGIN FreeBSD ARM
          - goos: freebsd
            goarch: arm64
          - goos: freebsd
            goarch: arm
            goarm: 7
          # END FreeBSD ARM
          # BEGIN S390X
          - goos: linux
            goarch: s390x
          # END S390X
          # END Other architectures
      fail-fast: false

    runs-on: ubuntu-latest
    env:
      GOOS: ${{ matrix.goos }}
      GOARCH: ${{ matrix.goarch }}
      GOARM: ${{ matrix.goarm }}
      CGO_ENABLED: 0
    steps:
      - name: Checkout codebase
        uses: actions/checkout@v3
      - name: Show workflow information
        id: get_filename
        run: |
          export _NAME=$(jq ".[\"$GOOS-$GOARCH$GOARM$GOMIPS\"].friendlyName" -r < .github/build/friendly-filenames.json)
          echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME"
          echo "::set-output name=ASSET_NAME::$_NAME"
          echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
      - name: Set up Go
        uses: actions/setup-go@v3
        with:
          go-version: '1.19'

      - name: Get project dependencies
        run: go mod download
      - name: Get release version
        if: ${{ github.event_name == 'release' }}
        run: |
          echo "version=$(echo $GITHUB_REF | cut -d / -f 3)" >> $GITHUB_ENV
      - name: Get other version
        if: ${{ github.event_name != 'release' }}
        run: |
          echo "version=${{ github.sha }}" >> $GITHUB_ENV
      - name: Build V2bX
        run: |
          echo "version: $version"
          mkdir -p build_assets
          go build -v -o build_assets/V2bX -tags "xray hy" -trimpath -ldflags "-X 'github.com/Yuzuki616/V2bX/cmd.version=$version' -s -w -buildid="

      - name: Build Mips softfloat V2bX
        if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'
        run: |
          echo "version: $version"
          GOMIPS=softfloat go build -v -o build_assets/V2bX_softfloat -tags "xray hy" -trimpath -ldflags "-X 'github.com/Yuzuki616/V2bX/cmd.version=$version' -s -w -buildid="
      - name: Rename Windows V2bX
        if: matrix.goos == 'windows'
        run: |
          cd ./build_assets || exit 1
          mv V2bX V2bX.exe
      - name: Prepare to release
        run: |
          cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
          cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
          cp ${GITHUB_WORKSPACE}/example/dns.json ./build_assets/dns.json
          cp ${GITHUB_WORKSPACE}/example/route.json ./build_assets/route.json
          cp ${GITHUB_WORKSPACE}/example/custom_outbound.json ./build_assets/custom_outbound.json
          cp ${GITHUB_WORKSPACE}/example/custom_inbound.json ./build_assets/custom_inbound.json
          cp ${GITHUB_WORKSPACE}/example/rulelist ./build_assets/rulelist
          cp ${GITHUB_WORKSPACE}/example/config.yml.example ./build_assets/config.yml
          LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
          for i in "${LIST[@]}"
          do
            INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
            FILE_NAME="${INFO[2]}.dat"
            echo -e "Downloading ${FILE_NAME}..."
            curl -L "https://github.com/v2fly/${INFO[0]}/releases/latest/download/${INFO[1]}.dat" -o ./build_assets/${FILE_NAME}
            echo -e "Verifying HASH key..."
            HASH="$(curl -sL "https://github.com/v2fly/${INFO[0]}/releases/latest/download/${INFO[1]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
            [ "$(sha256sum "./build_assets/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
          done
      - name: Create ZIP archive
        shell: bash
        run: |
          pushd build_assets || exit 1
          touch -mt $(date +%Y01010000) *
          zip -9vr ../V2bX-$ASSET_NAME.zip .
          popd || exit 1
          FILE=./V2bX-$ASSET_NAME.zip
          DGST=$FILE.dgst
          for METHOD in {"md5","sha1","sha256","sha512"}
          do
            openssl dgst -$METHOD $FILE | sed 's/([^)]*)//g' >>$DGST
          done
      - name: Change the name
        run: |
          mv build_assets V2bX-$ASSET_NAME
      - name: Upload files to Artifacts
        uses: actions/upload-artifact@v3
        with:
          name: V2bX-${{ steps.get_filename.outputs.ASSET_NAME }}
          path: |
            ./V2bX-${{ steps.get_filename.outputs.ASSET_NAME }}/*
      - name: Upload binaries to release
        uses: svenstaro/upload-release-action@v2
        if: github.event_name == 'release'
        with:
          repo_token: ${{ secrets.GITHUB_TOKEN }}
          file: ./V2bX-${{ steps.get_filename.outputs.ASSET_NAME }}.zip*
          tag: ${{ github.ref }}
          file_glob: true


================================================
FILE: .gitignore
================================================
example/config.yml
example/main
example/XrayR
example/XrayR*
example/mytest
example/access.logo
example/error.log
api/chooseparser.go.bak
app/Inboundbuilder/.lego/
app/legocmd/.lego/
.vscode/launch.json
example/.lego
example/cert
./vscode
.idea/*

================================================
FILE: LICENSE
================================================
Mozilla Public License Version 2.0
==================================

1. Definitions
--------------

1.1. "Contributor"
    means each individual or legal entity that creates, contributes to
    the creation of, or owns Covered Software.

1.2. "Contributor Version"
    means the combination of the Contributions of others (if any) used
    by a Contributor and that particular Contributor's Contribution.

1.3. "Contribution"
    means Covered Software of a particular Contributor.

1.4. "Covered Software"
    means Source Code Form to which the initial Contributor has attached
    the notice in Exhibit A, the Executable Form of such Source Code
    Form, and Modifications of such Source Code Form, in each case
    including portions thereof.

1.5. "Incompatible With Secondary Licenses"
    means

    (a) that the initial Contributor has attached the notice described
        in Exhibit B to the Covered Software; or

    (b) that the Covered Software was made available under the terms of
        version 1.1 or earlier of the License, but not also under the
        terms of a Secondary License.

1.6. "Executable Form"
    means any form of the work other than Source Code Form.

1.7. "Larger Work"
    means a work that combines Covered Software with other material, in
    a separate file or files, that is not Covered Software.

1.8. "License"
    means this document.

1.9. "Licensable"
    means having the right to grant, to the maximum extent possible,
    whether at the time of the initial grant or subsequently, any and
    all of the rights conveyed by this License.

1.10. "Modifications"
    means any of the following:

    (a) any file in Source Code Form that results from an addition to,
        deletion from, or modification of the contents of Covered
        Software; or

    (b) any new file in Source Code Form that contains any Covered
        Software.

1.11. "Patent Claims" of a Contributor
    means any patent claim(s), including without limitation, method,
    process, and apparatus claims, in any patent Licensable by such
    Contributor that would be infringed, but for the grant of the
    License, by the making, using, selling, offering for sale, having
    made, import, or transfer of either its Contributions or its
    Contributor Version.

1.12. "Secondary License"
    means either the GNU General Public License, Version 2.0, the GNU
    Lesser General Public License, Version 2.1, the GNU Affero General
    Public License, Version 3.0, or any later versions of those
    licenses.

1.13. "Source Code Form"
    means the form of the work preferred for making modifications.

1.14. "You" (or "Your")
    means an individual or a legal entity exercising rights under this
    License. For legal entities, "You" includes any entity that
    controls, is controlled by, or is under common control with You. For
    purposes of this definition, "control" means (a) the power, direct
    or indirect, to cause the direction or management of such entity,
    whether by contract or otherwise, or (b) ownership of more than
    fifty percent (50%) of the outstanding shares or beneficial
    ownership of such entity.

2. License Grants and Conditions
--------------------------------

2.1. Grants

Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:

(a) under intellectual property rights (other than patent or trademark)
    Licensable by such Contributor to use, reproduce, make available,
    modify, display, perform, distribute, and otherwise exploit its
    Contributions, either on an unmodified basis, with Modifications, or
    as part of a Larger Work; and

(b) under Patent Claims of such Contributor to make, use, sell, offer
    for sale, have made, import, and otherwise transfer either its
    Contributions or its Contributor Version.

2.2. Effective Date

The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.

2.3. Limitations on Grant Scope

The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:

(a) for any code that a Contributor has removed from Covered Software;
    or

(b) for infringements caused by: (i) Your and any other third party's
    modifications of Covered Software, or (ii) the combination of its
    Contributions with other software (except as part of its Contributor
    Version); or

(c) under Patent Claims infringed by Covered Software in the absence of
    its Contributions.

This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).

2.4. Subsequent Licenses

No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).

2.5. Representation

Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.

2.6. Fair Use

This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.

2.7. Conditions

Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.

3. Responsibilities
-------------------

3.1. Distribution of Source Form

All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.

3.2. Distribution of Executable Form

If You distribute Covered Software in Executable Form then:

(a) such Covered Software must also be made available in Source Code
    Form, as described in Section 3.1, and You must inform recipients of
    the Executable Form how they can obtain a copy of such Source Code
    Form by reasonable means in a timely manner, at a charge no more
    than the cost of distribution to the recipient; and

(b) You may distribute such Executable Form under the terms of this
    License, or sublicense it under different terms, provided that the
    license for the Executable Form does not attempt to limit or alter
    the recipients' rights in the Source Code Form under this License.

3.3. Distribution of a Larger Work

You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).

3.4. Notices

You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.

3.5. Application of Additional Terms

You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.

4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------

If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.

5. Termination
--------------

5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.

5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.

5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.

************************************************************************
*                                                                      *
*  6. Disclaimer of Warranty                                           *
*  -------------------------                                           *
*                                                                      *
*  Covered Software is provided under this License on an "as is"       *
*  basis, without warranty of any kind, either expressed, implied, or  *
*  statutory, including, without limitation, warranties that the       *
*  Covered Software is free of defects, merchantable, fit for a        *
*  particular purpose or non-infringing. The entire risk as to the     *
*  quality and performance of the Covered Software is with You.        *
*  Should any Covered Software prove defective in any respect, You     *
*  (not any Contributor) assume the cost of any necessary servicing,   *
*  repair, or correction. This disclaimer of warranty constitutes an   *
*  essential part of this License. No use of any Covered Software is   *
*  authorized under this License except under this disclaimer.         *
*                                                                      *
************************************************************************

************************************************************************
*                                                                      *
*  7. Limitation of Liability                                          *
*  --------------------------                                          *
*                                                                      *
*  Under no circumstances and under no legal theory, whether tort      *
*  (including negligence), contract, or otherwise, shall any           *
*  Contributor, or anyone who distributes Covered Software as          *
*  permitted above, be liable to You for any direct, indirect,         *
*  special, incidental, or consequential damages of any character      *
*  including, without limitation, damages for lost profits, loss of    *
*  goodwill, work stoppage, computer failure or malfunction, or any    *
*  and all other commercial damages or losses, even if such party      *
*  shall have been informed of the possibility of such damages. This   *
*  limitation of liability shall not apply to liability for death or   *
*  personal injury resulting from such party's negligence to the       *
*  extent applicable law prohibits such limitation. Some               *
*  jurisdictions do not allow the exclusion or limitation of           *
*  incidental or consequential damages, so this exclusion and          *
*  limitation may not apply to You.                                    *
*                                                                      *
************************************************************************

8. Litigation
-------------

Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.

9. Miscellaneous
----------------

This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.

10. Versions of the License
---------------------------

10.1. New Versions

Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.

10.2. Effect of New Versions

You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.

10.3. Modified Versions

If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).

10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses

If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.

Exhibit A - Source Code Form License Notice
-------------------------------------------

  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.

If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.

You may add additional accurate notices of copyright ownership.

Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------

  This Source Code Form is "Incompatible With Secondary Licenses", as
  defined by the Mozilla Public License, v. 2.0.


================================================
FILE: README.md
================================================
# V2bX
**项目转移至[V2bX](https://github.com/InazumaV/V2bX),此仓库不再维护**

[![](https://img.shields.io/badge/TgChat-%E4%BA%A4%E6%B5%81%E7%BE%A4-blue)](https://t.me/YuzukiProjects)

A V2board node server based on multi core, modified from XrayR.  
一个基于多种内核的V2board节点服务端,修改自XrayR,支持V2ay,Trojan,Shadowsocks协议。

**注意:1.1.0将更换为V2board1.7.0之后新增的Api,原Api将被移除,请1.7.0之前的用户使用1.1.0之前的版本。**

## 特点

* 永久开源且免费。
* 支持V2ray,Trojan, Shadowsocks多种协议。
* 支持Vless和XTLS等新特性。
* 支持单实例对接多节点,无需重复启动。
* 支持限制在线IP。
* 支持限制Tcp连接数。
* 支持节点端口级别、用户级别限速。
* 配置简单明了。
* 修改配置自动重启实例。
* 支持多种内核,易扩展。
* 支持条件编译,可仅编译需要的内核。

## 功能介绍

| 功能        | v2ray | trojan | shadowsocks | hysteria |
|-----------|-------|--------|-------------|----------|
| 自动申请tls证书 | √     | √      | √           | √        |
| 自动续签tls证书 | √     | √      | √           | √        |
| 在线人数统计    | √     | √      | √           | √        |
| 审计规则      | √     | √      | √           |          |
| 自定义DNS    | √     | √      | √           | √        |
| 在线IP数限制   | √     | √      | √           | √        |
| 连接数限制     | √     | √      | √           |          |
| 跨节点IP数限制  | √     | √      | √           |          |
| 按照用户限速    | √     | √      | √           |          |
| 动态限速(未测试) | √     | √      | √           |          |

## TODO

- [ ] 重新实现动态限速
- [ ] 重新实现在线IP同步(跨节点在线IP限制)
- [x] 集成基本操作Command(Start, Stop, Restart, Status, Uninstall)
- [ ] 完善Hysteria内核支持
- [ ] 完善使用文档
- [ ] 尽可能统一日志输出格式

## 软件安装

### 一键安装

```
wget -N https://raw.githubusercontents.com/Yuzuki616/V2bX-script/master/install.sh && bash install.sh
```

### 手动安装

[手动安装教程(过时待更新)](https://yuzuki-1.gitbook.io/v2bx-doc/xrayr-xia-zai-he-an-zhuang/install/manual)

## 构建
``` bash
# 通过-tag选项指定要编译的内核, 可选 xray, hy
go build -o V2bX -ldflags '-s -w' -gcflags="all=-trimpath=${PWD}" -asmflags="all=-trimpath=${PWD} -tags "xray hy"
```

## 配置文件及详细使用教程

[详细使用教程](https://yuzuki-1.gitbook.io/v2bx-doc/)

## 免责声明

* 此项目用于本人自用,因此本人不能保证向后兼容性。
* 由于本人能力有限,不能保证所有功能的可用性,如果出现问题请在Issues反馈。
* 本人不对任何人使用本项目造成的任何后果承担责任。
* 本人比较多变,因此本项目可能会随想法或思路的变动随性更改项目结构或大规模重构代码,若不能接受请勿使用。

## Thanks

* [Project X](https://github.com/XTLS/)
* [V2Fly](https://github.com/v2fly)
* [VNet-V2ray](https://github.com/ProxyPanel/VNet-V2ray)
* [Air-Universe](https://github.com/crossfw/Air-Universe)
* [XrayR](https://github.com/XrayR/XrayR)

## Stars 增长记录

[![Stargazers over time](https://starchart.cc/Yuzuki616/V2bX.svg)](https://starchart.cc/Yuzuki616/V2bX)


================================================
FILE: api/iprecoder/interface.go
================================================
package iprecoder

import (
	"github.com/Yuzuki616/V2bX/limiter"
)

type IpRecorder interface {
	SyncOnlineIp(Ips []limiter.UserIpList) ([]limiter.UserIpList, error)
}


================================================
FILE: api/iprecoder/recorder.go
================================================
package iprecoder

import (
	"errors"
	"github.com/Yuzuki616/V2bX/conf"
	"github.com/Yuzuki616/V2bX/limiter"
	"github.com/go-resty/resty/v2"
	"github.com/goccy/go-json"
	"time"
)

type Recorder struct {
	client *resty.Client
	*conf.RecorderConfig
}

func NewRecorder(c *conf.RecorderConfig) *Recorder {
	return &Recorder{
		client:         resty.New().SetTimeout(time.Duration(c.Timeout) * time.Second),
		RecorderConfig: c,
	}
}

func (r *Recorder) SyncOnlineIp(ips []limiter.UserIpList) ([]limiter.UserIpList, error) {
	rsp, err := r.client.R().
		SetBody(ips).
		Post(r.Url + "/api/v1/SyncOnlineIp?token=" + r.Token)
	if err != nil {
		return nil, err
	}
	if rsp.StatusCode() != 200 {
		return nil, errors.New(rsp.String())
	}
	ips = []limiter.UserIpList{}
	err = json.Unmarshal(rsp.Body(), &ips)
	if err != nil {
		return nil, err
	}
	return ips, nil
}


================================================
FILE: api/iprecoder/redis.go
================================================
package iprecoder

import (
	"context"
	"fmt"
	"github.com/Yuzuki616/V2bX/conf"
	"github.com/Yuzuki616/V2bX/limiter"
	"github.com/go-redis/redis/v8"
	"strconv"
	"time"
)

type Redis struct {
	*conf.RedisConfig
	client *redis.Client
}

func NewRedis(c *conf.RedisConfig) *Redis {
	return &Redis{
		RedisConfig: c,
		client: redis.NewClient(&redis.Options{
			Addr:     c.Address,
			Password: c.Password,
			DB:       c.Db,
		}),
	}
}

func (r *Redis) SyncOnlineIp(Ips []limiter.UserIpList) ([]limiter.UserIpList, error) {
	ctx := context.Background()
	for i := range Ips {
		err := r.client.SAdd(ctx, "UserList", Ips[i].Uid).Err()
		if err != nil {
			return nil, fmt.Errorf("add user failed: %s", err)
		}
		r.client.Expire(ctx, "UserList", time.Second*time.Duration(r.Expiry))
		for _, ip := range Ips[i].IpList {
			err := r.client.SAdd(ctx, strconv.Itoa(Ips[i].Uid), ip).Err()
			if err != nil {
				return nil, fmt.Errorf("add ip failed: %s", err)
			}
			r.client.Expire(ctx, strconv.Itoa(Ips[i].Uid), time.Second*time.Duration(r.Expiry))
		}
	}
	c := r.client.SMembers(ctx, "UserList")
	if c.Err() != nil {
		return nil, fmt.Errorf("get user list failed: %s", c.Err())
	}
	Ips = make([]limiter.UserIpList, 0, len(c.Val()))
	for _, uid := range c.Val() {
		uidInt, err := strconv.Atoi(uid)
		if err != nil {
			return nil, fmt.Errorf("convert uid failed: %s", err)
		}
		ips := r.client.SMembers(ctx, uid)
		if ips.Err() != nil {
			return nil, fmt.Errorf("get ip list failed: %s", ips.Err())
		}
		Ips = append(Ips, limiter.UserIpList{
			Uid:    uidInt,
			IpList: ips.Val(),
		})
	}
	return Ips, nil
}


================================================
FILE: api/iprecoder/redis_test.go
================================================
package iprecoder

import (
	"github.com/Yuzuki616/V2bX/conf"
	"github.com/Yuzuki616/V2bX/limiter"
	"log"
	"testing"
)

func TestRedis_SyncOnlineIp(t *testing.T) {
	r := NewRedis(&conf.RedisConfig{
		Address:  "127.0.0.1:6379",
		Password: "",
		Db:       0,
	})
	users, err := r.SyncOnlineIp([]limiter.UserIpList{
		{1, []string{"5.5.5.5", "4.4.4.4"}},
	})
	if err != nil {
		t.Fatal(err)
	}
	log.Println(users)
}


================================================
FILE: api/panel/node.go
================================================
package panel

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/Yuzuki616/V2bX/common/crypt"
	"github.com/goccy/go-json"
	log "github.com/sirupsen/logrus"
	coreConf "github.com/xtls/xray-core/infra/conf"
)

type CommonNodeRsp struct {
	Host       string     `json:"host"`
	ServerPort int        `json:"server_port"`
	ServerName string     `json:"server_name"`
	Routes     []Route    `json:"routes"`
	BaseConfig BaseConfig `json:"base_config"`
}

type Route struct {
	Id     int         `json:"id"`
	Match  interface{} `json:"match"`
	Action string      `json:"action"`
	//ActionValue interface{} `json:"action_value"`
}
type BaseConfig struct {
	PushInterval any `json:"push_interval"`
	PullInterval any `json:"pull_interval"`
}

type V2rayNodeRsp struct {
	Tls             int             `json:"tls"`
	Network         string          `json:"network"`
	NetworkSettings json.RawMessage `json:"networkSettings"`
	ServerName      string          `json:"server_name"`
}

type ShadowsocksNodeRsp struct {
	Cipher    string `json:"cipher"`
	ServerKey string `json:"server_key"`
}

type HysteriaNodeRsp struct {
	UpMbps   int    `json:"up_mbps"`
	DownMbps int    `json:"down_mbps"`
	Obfs     string `json:"obfs"`
}

type NodeInfo struct {
	Id              int
	Type            string
	Rules           Rules
	Host            string
	Port            int
	Network         string
	ExtraConfig     V2rayExtraConfig
	NetworkSettings json.RawMessage
	Tls             bool
	ServerName      string
	UpMbps          int
	DownMbps        int
	ServerKey       string
	Cipher          string
	HyObfs          string
	PushInterval    time.Duration
	PullInterval    time.Duration
}

type Rules struct {
	Regexp   []string
	Protocol []string
}

type V2rayExtraConfig struct {
	EnableVless   string         `json:"EnableVless"`
	VlessFlow     string         `json:"VlessFlow"`
	EnableReality string         `json:"EnableReality"`
	RealityConfig *RealityConfig `json:"RealityConfig"`
}

type RealityConfig struct {
	Dest         interface{} `yaml:"Dest" json:"Dest"`
	Xver         string      `yaml:"Xver" json:"Xver"`
	ServerNames  []string    `yaml:"ServerNames" json:"ServerNames"`
	PrivateKey   string      `yaml:"PrivateKey" json:"PrivateKey"`
	MinClientVer string      `yaml:"MinClientVer" json:"MinClientVer"`
	MaxClientVer string      `yaml:"MaxClientVer" json:"MaxClientVer"`
	MaxTimeDiff  string      `yaml:"MaxTimeDiff" json:"MaxTimeDiff"`
	ShortIds     []string    `yaml:"ShortIds" json:"ShortIds"`
}

func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
	const path = "/api/v1/server/UniProxy/config"
	r, err := c.client.
		R().
		SetHeader("If-None-Match", c.nodeEtag).
		Get(path)
	if err = c.checkResponse(r, path, err); err != nil {
		return
	}
	if r.StatusCode() == 304 {
		return nil, nil
	}
	// parse common params
	node = &NodeInfo{
		Id:   c.NodeId,
		Type: c.NodeType,
	}
	common := CommonNodeRsp{}
	err = json.Unmarshal(r.Body(), &common)
	if err != nil {
		return nil, fmt.Errorf("decode common params error: %s", err)
	}
	for i := range common.Routes { // parse rules from routes
		var matchs []string
		if _, ok := common.Routes[i].Match.(string); ok {
			matchs = strings.Split(common.Routes[i].Match.(string), ",")
		} else if _, ok = common.Routes[i].Match.([]string); ok {
			matchs = common.Routes[i].Match.([]string)
		} else {
			temp := common.Routes[i].Match.([]interface{})
			matchs = make([]string, len(temp))
			for i := range temp {
				matchs[i] = temp[i].(string)
			}
		}
		switch common.Routes[i].Action {
		case "block":
			for _, v := range matchs {
				if strings.HasPrefix(v, "protocol:") {
					// protocol
					node.Rules.Protocol = append(node.Rules.Protocol, strings.TrimPrefix(v, "protocol:"))
				} else {
					// domain
					node.Rules.Regexp = append(node.Rules.Regexp, strings.TrimPrefix(v, "regexp:"))
				}
			}
		case "dns":
			if matchs[0] != "main" {
				break
			}
			dnsPath := os.Getenv("XRAY_DNS_PATH")
			if dnsPath == "" {
				break
			}
			dns := []byte(strings.Join(matchs[1:], ""))
			currentData, err := os.ReadFile(dnsPath)
			if err != nil {
				log.WithField("err", err).Panic("Failed to read XRAY_DNS_PATH")
				break
			}
			if !bytes.Equal(currentData, dns) {
				coreDnsConfig := &coreConf.DNSConfig{}
				if err = json.NewDecoder(bytes.NewReader(dns)).Decode(coreDnsConfig); err != nil {
					log.WithField("err", err).Panic("Failed to unmarshal DNS config")
				}
				_, err := coreDnsConfig.Build()
				if err != nil {
					log.WithField("err", err).Panic("Failed to understand DNS config, Please check: https://xtls.github.io/config/dns.html for help")
					break
				}
				if err = os.Truncate(dnsPath, 0); err != nil {
					log.WithField("err", err).Panic("Failed to clear XRAY DNS PATH file")
				}
				if err = os.WriteFile(dnsPath, dns, 0644); err != nil {
					log.WithField("err", err).Panic("Failed to write DNS to XRAY DNS PATH file")
				}
			}
		}
	}
	node.ServerName = common.ServerName
	node.Host = common.Host
	node.Port = common.ServerPort
	node.PullInterval = intervalToTime(common.BaseConfig.PullInterval)
	node.PushInterval = intervalToTime(common.BaseConfig.PushInterval)
	// parse protocol params
	switch c.NodeType {
	case "v2ray":
		rsp := V2rayNodeRsp{}
		err = json.Unmarshal(r.Body(), &rsp)
		if err != nil {
			return nil, fmt.Errorf("decode v2ray params error: %s", err)
		}
		node.Network = rsp.Network
		node.NetworkSettings = rsp.NetworkSettings
		node.ServerName = rsp.ServerName
		if rsp.Tls == 1 {
			node.Tls = true
		}
		err = json.Unmarshal(rsp.NetworkSettings, &node.ExtraConfig)
		if err != nil {
			return nil, fmt.Errorf("decode v2ray extra error: %s", err)
		}
		if node.ExtraConfig.EnableReality == "true" {
			if node.ExtraConfig.RealityConfig == nil {
				node.ExtraConfig.EnableReality = "false"
			} else {
				key := crypt.GenX25519Private([]byte(strconv.Itoa(c.NodeId) + c.NodeType + c.Token +
					node.ExtraConfig.RealityConfig.PrivateKey))
				node.ExtraConfig.RealityConfig.PrivateKey = base64.RawURLEncoding.EncodeToString(key)
			}
		}
	case "shadowsocks":
		rsp := ShadowsocksNodeRsp{}
		err = json.Unmarshal(r.Body(), &rsp)
		if err != nil {
			return nil, fmt.Errorf("decode v2ray params error: %s", err)
		}
		node.ServerKey = rsp.ServerKey
		node.Cipher = rsp.Cipher
	case "trojan":
		node.Tls = true
	case "hysteria":
		rsp := HysteriaNodeRsp{}
		err = json.Unmarshal(r.Body(), &rsp)
		if err != nil {
			return nil, fmt.Errorf("decode v2ray params error: %s", err)
		}
		node.DownMbps = rsp.DownMbps
		node.UpMbps = rsp.UpMbps
		node.HyObfs = rsp.Obfs
	}
	c.nodeEtag = r.Header().Get("ETag")
	return
}

func intervalToTime(i interface{}) time.Duration {
	switch reflect.TypeOf(i).Kind() {
	case reflect.Int:
		return time.Duration(i.(int)) * time.Second
	case reflect.String:
		i, _ := strconv.Atoi(i.(string))
		return time.Duration(i) * time.Second
	case reflect.Float64:
		return time.Duration(i.(float64)) * time.Second
	default:
		return time.Duration(reflect.ValueOf(i).Int()) * time.Second
	}
}


================================================
FILE: api/panel/node_test.go
================================================
package panel

import (
	"github.com/Yuzuki616/V2bX/conf"
	"log"
	"testing"
)

var client *Client

func init() {
	c, err := New(&conf.ApiConfig{
		APIHost:  "http://127.0.0.1",
		Key:      "token",
		NodeType: "V2ray",
		NodeID:   1,
	})
	if err != nil {
		log.Panic(err)
	}
	client = c
}

func TestClient_GetNodeInfo(t *testing.T) {
	log.Println(client.GetNodeInfo())
	log.Println(client.GetNodeInfo())
}

func TestClient_ReportUserTraffic(t *testing.T) {
	log.Println(client.ReportUserTraffic([]UserTraffic{
		{
			UID:      10372,
			Upload:   1000,
			Download: 1000,
		},
	}))
}


================================================
FILE: api/panel/panel.go
================================================
package panel

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/Yuzuki616/V2bX/conf"
	"github.com/go-resty/resty/v2"
)

// Panel is the interface for different panel's api.

type Client struct {
	client        *resty.Client
	APIHost       string
	Token         string
	NodeType      string
	NodeId        int
	LocalRuleList []*regexp.Regexp
	nodeEtag      string
	userEtag      string
}

func New(c *conf.ApiConfig) (*Client, error) {
	client := resty.New()
	client.SetRetryCount(3)
	if c.Timeout > 0 {
		client.SetTimeout(time.Duration(c.Timeout) * time.Second)
	} else {
		client.SetTimeout(5 * time.Second)
	}
	client.OnError(func(req *resty.Request, err error) {
		if v, ok := err.(*resty.ResponseError); ok {
			// v.Response contains the last response from the server
			// v.Err contains the original error
			log.Print(v.Err)
		}
	})
	client.SetBaseURL(c.APIHost)
	// Check node type
	c.NodeType = strings.ToLower(c.NodeType)
	switch c.NodeType {
	case "v2ray", "trojan", "shadowsocks", "hysteria":
	default:
		return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
	}
	// set params
	client.SetQueryParams(map[string]string{
		"node_type": c.NodeType,
		"node_id":   strconv.Itoa(c.NodeID),
		"token":     c.Key,
	})
	// Read local rule list
	localRuleList := readLocalRuleList(c.RuleListPath)
	return &Client{
		client:        client,
		Token:         c.Key,
		APIHost:       c.APIHost,
		NodeType:      c.NodeType,
		NodeId:        c.NodeID,
		LocalRuleList: localRuleList,
	}, nil
}

// readLocalRuleList reads the local rule list file
func readLocalRuleList(path string) (LocalRuleList []*regexp.Regexp) {
	LocalRuleList = make([]*regexp.Regexp, 0)
	if path != "" {
		// open the file
		file, err := os.Open(path)
		//handle errors while opening
		if err != nil {
			log.Printf("Error when opening file: %s", err)
			return
		}
		fileScanner := bufio.NewScanner(file)
		// read line by line
		for fileScanner.Scan() {
			LocalRuleList = append(LocalRuleList, regexp.MustCompile(fileScanner.Text()))
		}
		// handle first encountered error while reading
		if err := fileScanner.Err(); err != nil {
			log.Fatalf("Error while reading file: %s", err)
			return
		}
	}
	return
}


================================================
FILE: api/panel/user.go
================================================
package panel

import (
	"fmt"

	"github.com/goccy/go-json"
)

type OnlineUser struct {
	UID int
	IP  string
}

type UserInfo struct {
	Id         int    `json:"id"`
	Uuid       string `json:"uuid"`
	SpeedLimit int    `json:"speed_limit"`
}

type UserListBody struct {
	//Msg  string `json:"msg"`
	Users []UserInfo `json:"users"`
}

// GetUserList will pull user form sspanel
func (c *Client) GetUserList() (UserList []UserInfo, err error) {
	const path = "/api/v1/server/UniProxy/user"
	r, err := c.client.R().
		SetHeader("If-None-Match", c.userEtag).
		Get(path)
	err = c.checkResponse(r, path, err)
	if err != nil {
		return nil, err
	}
	err = c.checkResponse(r, path, err)
	if r.StatusCode() == 304 {
		return nil, nil
	}
	var userList *UserListBody
	err = json.Unmarshal(r.Body(), &userList)
	if err != nil {
		return nil, fmt.Errorf("unmarshal userlist error: %s", err)
	}
	c.userEtag = r.Header().Get("ETag")
	return userList.Users, nil
}

type UserTraffic struct {
	UID      int
	Upload   int64
	Download int64
}

// ReportUserTraffic reports the user traffic
func (c *Client) ReportUserTraffic(userTraffic []UserTraffic) error {
	data := make(map[int][]int64, len(userTraffic))
	for i := range userTraffic {
		data[userTraffic[i].UID] = []int64{userTraffic[i].Upload, userTraffic[i].Download}
	}
	const path = "/api/v1/server/UniProxy/push"
	r, err := c.client.R().
		SetBody(data).
		ForceContentType("application/json").
		Post(path)
	err = c.checkResponse(r, path, err)
	if err != nil {
		return err
	}
	return nil
}


================================================
FILE: api/panel/utils.go
================================================
package panel

import (
	"fmt"
	"github.com/go-resty/resty/v2"
	path2 "path"
)

// Debug set the client debug for client
func (c *Client) Debug() {
	c.client.SetDebug(true)
}

func (c *Client) assembleURL(path string) string {
	return path2.Join(c.APIHost + path)
}
func (c *Client) checkResponse(res *resty.Response, path string, err error) error {
	if err != nil {
		return fmt.Errorf("request %s failed: %s", c.assembleURL(path), err)
	}
	if res.StatusCode() >= 400 {
		body := res.Body()
		return fmt.Errorf("request %s failed: %s", c.assembleURL(path), string(body))
	}
	return nil
}


================================================
FILE: cmd/action_linux.go
================================================
package cmd

import (
	"fmt"
	"github.com/Yuzuki616/V2bX/common/exec"
	"github.com/spf13/cobra"
	"time"
)

var (
	startCommand = cobra.Command{
		Use:   "start",
		Short: "Start V2bX service",
		Run:   startHandle,
	}
	stopCommand = cobra.Command{
		Use:   "stop",
		Short: "Stop V2bX service",
		Run:   stopHandle,
	}
	restartCommand = cobra.Command{
		Use:   "restart",
		Short: "Restart V2bX service",
		Run:   restartHandle,
	}
	logCommand = cobra.Command{
		Use:   "log",
		Short: "Output V2bX log",
		Run: func(_ *cobra.Command, _ []string) {
			exec.RunCommandStd("journalctl", "-u", "V2bX.service", "-e", "--no-pager", "-f")
		},
	}
)

func init() {
	command.AddCommand(&startCommand)
	command.AddCommand(&stopCommand)
	command.AddCommand(&restartCommand)
	command.AddCommand(&logCommand)
}

func startHandle(_ *cobra.Command, _ []string) {
	r, err := checkRunning()
	if err != nil {
		fmt.Println(Err("check status error: ", err))
		fmt.Println(Err("V2bX启动失败"))
		return
	}
	if r {
		fmt.Println(Ok("V2bX已运行,无需再次启动,如需重启请选择重启"))
	}
	_, err = exec.RunCommandByShell("systemctl start V2bX.service")
	if err != nil {
		fmt.Println(Err("exec start cmd error: ", err))
		fmt.Println(Err("V2bX启动失败"))
		return
	}
	time.Sleep(time.Second * 3)
	r, err = checkRunning()
	if err != nil {
		fmt.Println(Err("check status error: ", err))
		fmt.Println(Err("V2bX启动失败"))
	}
	if !r {
		fmt.Println(Err("V2bX可能启动失败,请稍后使用 V2bX log 查看日志信息"))
		return
	}
	fmt.Println(Ok("V2bX 启动成功,请使用 V2bX log 查看运行日志"))
}

func stopHandle(_ *cobra.Command, _ []string) {
	_, err := exec.RunCommandByShell("systemctl stop V2bX.service")
	if err != nil {
		fmt.Println(Err("exec stop cmd error: ", err))
		fmt.Println(Err("V2bX停止失败"))
		return
	}
	time.Sleep(2 * time.Second)
	r, err := checkRunning()
	if err != nil {
		fmt.Println(Err("check status error:", err))
		fmt.Println(Err("V2bX停止失败"))
		return
	}
	if r {
		fmt.Println(Err("V2bX停止失败,可能是因为停止时间超过了两秒,请稍后查看日志信息"))
		return
	}
	fmt.Println(Ok("V2bX 停止成功"))
}

func restartHandle(_ *cobra.Command, _ []string) {
	_, err := exec.RunCommandByShell("systemctl restart V2bX.service")
	if err != nil {
		fmt.Println(Err("exec restart cmd error: ", err))
		fmt.Println(Err("V2bX重启失败"))
		return
	}
	r, err := checkRunning()
	if err != nil {
		fmt.Println(Err("check status error: ", err))
		fmt.Println(Err("V2bX重启失败"))
		return
	}
	if !r {
		fmt.Println(Err("V2bX可能启动失败,请稍后使用 V2bX log 查看日志信息"))
		return
	}
	fmt.Println(Ok("V2bX重启成功"))
}


================================================
FILE: cmd/cmd.go
================================================
package cmd

import (
	log "github.com/sirupsen/logrus"

	_ "github.com/Yuzuki616/V2bX/core/imports"
	"github.com/spf13/cobra"
)

var command = &cobra.Command{
	Use: "V2bX",
}

func Run() {
	err := command.Execute()
	if err != nil {
		log.WithField("err", err).Error("Execute command failed")
	}
}


================================================
FILE: cmd/common.go
================================================
package cmd

import (
	"fmt"
	"github.com/Yuzuki616/V2bX/common/exec"
	"strings"
)

const (
	red    = "\033[0;31m"
	green  = "\033[0;32m"
	yellow = "\033[0;33m"
	plain  = "\033[0m"
)

func checkRunning() (bool, error) {
	o, err := exec.RunCommandByShell("systemctl status V2bX | grep Active")
	if err != nil {
		return false, err
	}
	return strings.Contains(o, "running"), nil
}

func Err(msg ...any) string {
	return red + fmt.Sprint(msg...) + plain
}

func Ok(msg ...any) string {
	return green + fmt.Sprint(msg...) + plain
}

func Warn(msg ...any) string {
	return yellow + fmt.Sprint(msg...) + plain
}


================================================
FILE: cmd/common_test.go
================================================
package cmd

import "testing"

func Test_printFailed(t *testing.T) {
	t.Log(Err("test"))
}


================================================
FILE: cmd/install_linux.go
================================================
package cmd

import (
	"fmt"
	"github.com/Yuzuki616/V2bX/common/exec"
	"github.com/spf13/cobra"
	"os"
	"strings"
)

var targetVersion string

var (
	updateCommand = cobra.Command{
		Use:   "update",
		Short: "Update V2bX version",
		Run: func(_ *cobra.Command, _ []string) {
			exec.RunCommandStd("bash",
				"<(curl -Ls https://raw.githubusercontents.com/Yuzuki616/V2bX-script/master/install.sh)",
				targetVersion)
		},
		Args: cobra.NoArgs,
	}
	uninstallCommand = cobra.Command{
		Use:   "uninstall",
		Short: "Uninstall V2bX",
		Run:   uninstallHandle,
	}
)

func init() {
	updateCommand.PersistentFlags().StringVar(&targetVersion, "version", "", "update target version")
	command.AddCommand(&updateCommand)
	command.AddCommand(&uninstallCommand)
}

func uninstallHandle(_ *cobra.Command, _ []string) {
	var yes string
	fmt.Println(Warn("确定要卸载 V2bX 吗?(Y/n)"))
	fmt.Scan(&yes)
	if strings.ToLower(yes) != "y" {
		fmt.Println("已取消卸载")
	}
	_, err := exec.RunCommandByShell("systemctl stop V2bX&&systemctl disable V2bX")
	if err != nil {
		fmt.Println(Err("exec cmd error: ", err))
		fmt.Println(Err("卸载失败"))
		return
	}
	_ = os.RemoveAll("/etc/systemd/system/V2bX.service")
	_ = os.RemoveAll("/etc/V2bX/")
	_ = os.RemoveAll("/usr/local/V2bX/")
	_ = os.RemoveAll("/bin/V2bX")
	_, err = exec.RunCommandByShell("systemctl daemon-reload&&systemctl reset-failed")
	if err != nil {
		fmt.Println(Err("exec cmd error: ", err))
		fmt.Println(Err("卸载失败"))
		return
	}
	fmt.Println(Ok("卸载成功"))
}


================================================
FILE: cmd/server.go
================================================
package cmd

import (
	log "github.com/sirupsen/logrus"
	"os"
	"os/signal"
	"runtime"
	"syscall"

	vCore "github.com/Yuzuki616/V2bX/core"

	"github.com/Yuzuki616/V2bX/conf"
	"github.com/Yuzuki616/V2bX/limiter"
	"github.com/Yuzuki616/V2bX/node"
	"github.com/spf13/cobra"
)

var (
	config string
	watch  bool
)

var serverCommand = cobra.Command{
	Use:   "server",
	Short: "Run V2bX server",
	Run:   serverHandle,
	Args:  cobra.NoArgs,
}

func init() {
	serverCommand.PersistentFlags().
		StringVarP(&config, "config", "c",
			"/etc/V2bX/config.yml", "config file path")
	serverCommand.PersistentFlags().
		BoolVarP(&watch, "watch", "w",
			true, "watch file path change")
	command.AddCommand(&serverCommand)
}

func serverHandle(_ *cobra.Command, _ []string) {
	showVersion()
	c := conf.New()
	err := c.LoadFromPath(config)
	if err != nil {
		log.WithField("err", err).Error("Load config file failed")
		return
	}
	limiter.Init()
	log.Info("Start V2bX...")
	vc, err := vCore.NewCore(&c.CoreConfig)
	if err != nil {
		log.WithField("err", err).Error("new core failed")
		return
	}
	err = vc.Start()
	if err != nil {
		log.WithField("err", err).Error("Start core failed")
		return
	}
	defer vc.Close()
	nodes := node.New()
	err = nodes.Start(c.NodesConfig, vc)
	if err != nil {
		log.WithField("err", err).Error("Run nodes failed")
		return
	}
	dns := os.Getenv("XRAY_DNS_PATH")
	if watch {
		err = c.Watch(config, dns, func() {
			nodes.Close()
			err = vc.Close()
			if err != nil {
				log.WithField("err", err).Error("Restart node failed")
				return
			}
			vc, err = vCore.NewCore(&c.CoreConfig)
			if err != nil {
				log.WithField("err", err).Error("New core failed")
				return
			}
			err = vc.Start()
			if err != nil {
				log.WithField("err", err).Error("Start core failed")
				return
			}
			err = nodes.Start(c.NodesConfig, vc)
			if err != nil {
				log.WithField("err", err).Error("Run nodes failed")
				return
			}
			runtime.GC()
		})
		if err != nil {
			log.WithField("err", err).Error("start watch failed")
			return
		}
	}
	// clear memory
	runtime.GC()
	// wait exit signal
	{
		osSignals := make(chan os.Signal, 1)
		signal.Notify(osSignals, syscall.SIGINT, syscall.SIGKILL, syscall.SIGTERM)
		<-osSignals
	}
}


================================================
FILE: cmd/server_test.go
================================================
package cmd

import "testing"

func TestRun(t *testing.T) {
	Run()
}


================================================
FILE: cmd/synctime.go
================================================
package cmd

import (
	"fmt"

	"github.com/Yuzuki616/V2bX/common/systime"
	"github.com/beevik/ntp"
	"github.com/spf13/cobra"
)

var ntpServer string

var commandSyncTime = &cobra.Command{
	Use:   "synctime",
	Short: "Sync time from ntp server",
	Args:  cobra.NoArgs,
	Run:   synctimeHandle,
}

func init() {
	commandSyncTime.Flags().StringVar(&ntpServer, "server", "time.apple.com", "ntp server")
	command.AddCommand(commandSyncTime)
}

func synctimeHandle(_ *cobra.Command, _ []string) {
	t, err := ntp.Time(ntpServer)
	if err != nil {
		fmt.Println(Err("get time from server error: ", err))
		fmt.Println(Err("同步时间失败"))
		return
	}
	err = systime.SetSystemTime(t)
	if err != nil {
		fmt.Println(Err("set system time error: ", err))
		fmt.Println(Err("同步时间失败"))
		return
	}
	fmt.Println(Ok("同步时间成功"))
}


================================================
FILE: cmd/version.go
================================================
package cmd

import (
	"fmt"
	"strings"

	vCore "github.com/Yuzuki616/V2bX/core"
	"github.com/spf13/cobra"
)

var (
	version  = "TempVersion" //use ldflags replace
	codename = "V2bX"
	intro    = "A V2board backend based on multi core"
)

var versionCommand = cobra.Command{
	Use:   "version",
	Short: "Print version info",
	Run: func(_ *cobra.Command, _ []string) {
		showVersion()
	},
}

func init() {
	command.AddCommand(&versionCommand)
}

func showVersion() {
	fmt.Printf("%s %s (%s) \n", codename, version, intro)
	fmt.Printf("Supported cores: %s\n", strings.Join(vCore.RegisteredCore(), ", "))
	// Warning
	fmt.Println(Warn("This version need V2board version >= 1.7.0."))
	fmt.Println(Warn("The version have many changed for config, please check your config file"))
}


================================================
FILE: cmd/x25519.go
================================================
package cmd

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"strings"

	"github.com/Yuzuki616/V2bX/common/crypt"

	"github.com/spf13/cobra"
	"golang.org/x/crypto/curve25519"
)

var x25519Command = cobra.Command{
	Use:   "x25519",
	Short: "Generate key pair for x25519 key exchange",
	Run: func(cmd *cobra.Command, args []string) {
		executeX25519()
	},
}

func init() {
	command.AddCommand(&x25519Command)
}

func executeX25519() {
	var output string
	var err error
	defer func() {
		fmt.Println(output)
	}()
	var privateKey []byte
	var publicKey []byte
	var yes, key string
	fmt.Println("要基于节点信息生成密钥吗?(Y/n)")
	fmt.Scan(&yes)
	if strings.ToLower(yes) == "y" {
		var temp string
		fmt.Println("请输入节点id:")
		fmt.Scan(&temp)
		key = temp
		fmt.Println("请输入节点类型:")
		fmt.Scan(&temp)
		key += strings.ToLower(temp)
		fmt.Println("请输入Token:")
		fmt.Scan(&temp)
		key += temp
		privateKey = crypt.GenX25519Private([]byte(key))
	} else {
		privateKey = make([]byte, curve25519.ScalarSize)
		if _, err = rand.Read(privateKey); err != nil {
			output = Err("read rand error: ", err)
			return
		}
	}
	if publicKey, err = curve25519.X25519(privateKey, curve25519.Basepoint); err != nil {
		output = Err("gen X25519 error: ", err)
		return
	}
	p := base64.RawURLEncoding.EncodeToString(privateKey)
	output = fmt.Sprint("Private key: ",
		p,
		"\nPublic key: ",
		base64.RawURLEncoding.EncodeToString(publicKey))
}


================================================
FILE: cmd/x25519_test.go
================================================
package cmd

import "testing"

func Test_executeX25519(t *testing.T) {
	executeX25519()
}


================================================
FILE: common/crypt/aes.go
================================================
package crypt

import (
	"crypto/aes"
	"encoding/base64"
)

func AesEncrypt(data []byte, key []byte) (string, error) {
	a, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}
	en := make([]byte, len(data))
	a.Encrypt(en, data)
	return base64.StdEncoding.EncodeToString(en), nil
}

func AesDecrypt(data string, key []byte) (string, error) {
	d, err := base64.StdEncoding.DecodeString(data)
	if err != nil {
		return "", err
	}
	a, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}
	de := make([]byte, len(data))
	a.Decrypt(de, d)
	return string(de), nil
}


================================================
FILE: common/crypt/x25519.go
================================================
package crypt

import (
	"crypto/sha256"
)

func GenX25519Private(data []byte) []byte {
	key := sha256.Sum256(data)
	key[0] &= 248
	key[31] &= 127
	key[31] |= 64
	return key[:32]
}


================================================
FILE: common/exec/exec.go
================================================
package exec

import (
	"errors"
	"os"
	"os/exec"
)

func RunCommandByShell(cmd string) (string, error) {
	e := exec.Command("bash", "-c", cmd)
	out, err := e.CombinedOutput()
	if errors.Unwrap(err) == exec.ErrNotFound {
		e = exec.Command("sh", "-c", cmd)
		out, err = e.CombinedOutput()
	}
	return string(out), err
}

func RunCommandStd(name string, args ...string) {
	e := exec.Command(name, args...)
	e.Stdout = os.Stdout
	e.Stdin = os.Stdin
	e.Stderr = os.Stderr
	_ = e.Run()
}


================================================
FILE: common/file/file.go
================================================
package file

import "os"

func IsExist(path string) bool {
	_, err := os.Stat(path)
	return err == nil || !os.IsNotExist(err)
}


================================================
FILE: common/format/user.go
================================================
package format

import (
	"fmt"
)

func UserTag(tag string, uuid string) string {
	return fmt.Sprintf("%s|%s", tag, uuid)
}


================================================
FILE: common/rate/rate.go
================================================
package rate

import (
	"github.com/juju/ratelimit"
	"github.com/xtls/xray-core/common"
	"github.com/xtls/xray-core/common/buf"
)

type Writer struct {
	writer  buf.Writer
	limiter *ratelimit.Bucket
}

func NewRateLimitWriter(writer buf.Writer, limiter *ratelimit.Bucket) buf.Writer {
	return &Writer{
		writer:  writer,
		limiter: limiter,
	}
}

func (w *Writer) Close() error {
	return common.Close(w.writer)
}

func (w *Writer) WriteMultiBuffer(mb buf.MultiBuffer) error {
	w.limiter.Wait(int64(mb.Len()))
	return w.writer.WriteMultiBuffer(mb)
}


================================================
FILE: common/systime/time_stub.go
================================================
//go:build !(windows || linux || darwin)

package systime

import (
	"os"
	"time"
)

func SetSystemTime(nowTime time.Time) error {
	return os.ErrInvalid
}


================================================
FILE: common/systime/time_unix.go
================================================
//go:build linux || darwin

package systime

import (
	"time"

	"golang.org/x/sys/unix"
)

func SetSystemTime(nowTime time.Time) error {
	timeVal := unix.NsecToTimeval(nowTime.UnixNano())
	return unix.Settimeofday(&timeVal)
}


================================================
FILE: common/systime/time_windows.go
================================================
package systime

import (
	"time"
	"unsafe"

	"golang.org/x/sys/windows"
)

func SetSystemTime(nowTime time.Time) error {
	var systemTime windows.Systemtime
	systemTime.Year = uint16(nowTime.Year())
	systemTime.Month = uint16(nowTime.Month())
	systemTime.Day = uint16(nowTime.Day())
	systemTime.Hour = uint16(nowTime.Hour())
	systemTime.Minute = uint16(nowTime.Minute())
	systemTime.Second = uint16(nowTime.Second())
	systemTime.Milliseconds = uint16(nowTime.UnixMilli() - nowTime.Unix()*1000)

	dllKernel32 := windows.NewLazySystemDLL("kernel32.dll")
	proc := dllKernel32.NewProc("SetSystemTime")

	_, _, err := proc.Call(
		uintptr(unsafe.Pointer(&systemTime)),
	)

	if err != nil && err.Error() != "The operation completed successfully." {
		return err
	}

	return nil
}


================================================
FILE: common/task/task.go
================================================
package task

import (
	"sync"
	"time"
)

// Task is a task that runs periodically.
type Task struct {
	// Interval of the task being run
	Interval time.Duration
	// Execute is the task function
	Execute func() error

	access  sync.Mutex
	timer   *time.Timer
	running bool
}

func (t *Task) hasClosed() bool {
	t.access.Lock()
	defer t.access.Unlock()

	return !t.running
}

func (t *Task) checkedExecute(first bool) error {
	if t.hasClosed() {
		return nil
	}

	t.access.Lock()
	defer t.access.Unlock()
	if first {
		if err := t.Execute(); err != nil {
			t.running = false
			return err
		}
	}
	if !t.running {
		return nil
	}
	t.timer = time.AfterFunc(t.Interval, func() {
		t.checkedExecute(true)
	})

	return nil
}

// Start implements common.Runnable.
func (t *Task) Start(first bool) error {
	t.access.Lock()
	if t.running {
		t.access.Unlock()
		return nil
	}
	t.running = true
	t.access.Unlock()
	if err := t.checkedExecute(first); err != nil {
		t.access.Lock()
		t.running = false
		t.access.Unlock()
		return err
	}
	return nil
}

// Close implements common.Closable.
func (t *Task) Close() {
	t.access.Lock()
	defer t.access.Unlock()

	t.running = false
	if t.timer != nil {
		t.timer.Stop()
		t.timer = nil
	}
}


================================================
FILE: common/task/task_test.go
================================================
package task

import (
	"log"
	"testing"
	"time"
)

func TestTask(t *testing.T) {
	ts := Task{Execute: func() error {
		log.Println("q")
		return nil
	}, Interval: time.Second}
	ts.Start(false)
}


================================================
FILE: conf/conf.go
================================================
package conf

import (
	"fmt"
	"gopkg.in/yaml.v3"
	"io"
	"os"
)

type Conf struct {
	CoreConfig  CoreConfig    `yaml:"CoreConfig"`
	NodesConfig []*NodeConfig `yaml:"Nodes"`
}

func New() *Conf {
	return &Conf{
		CoreConfig: CoreConfig{
			Type: "xray",
			XrayConfig: &XrayConfig{
				LogConfig:          NewLogConfig(),
				AssetPath:          "/etc/V2bX/",
				DnsConfigPath:      "",
				InboundConfigPath:  "",
				OutboundConfigPath: "",
				RouteConfigPath:    "",
				ConnectionConfig:   NewConnectionConfig(),
			},
		},
		NodesConfig: []*NodeConfig{},
	}
}

func (p *Conf) LoadFromPath(filePath string) error {
	f, err := os.Open(filePath)
	if err != nil {
		return fmt.Errorf("open config file error: %s", err)
	}
	defer f.Close()
	content, err := io.ReadAll(f)
	if err != nil {
		return fmt.Errorf("read file error: %s", err)
	}
	err = yaml.Unmarshal(content, p)
	if err != nil {
		return fmt.Errorf("decode config error: %s", err)
	}
	old := &OldConfig{}
	err = yaml.Unmarshal(content, old)
	if err == nil {
		migrateOldConfig(p, old)
	}
	return nil
}


================================================
FILE: conf/conf_test.go
================================================
package conf

import (
	"log"
	"testing"
)

func TestConf_LoadFromPath(t *testing.T) {
	c := New()
	t.Log(c.LoadFromPath("../example/config.yml.example"))
}

func TestConf_Watch(t *testing.T) {
	c := New()
	log.Println(c.Watch("../example/config.yml.example", func() {
		log.Println(1)
	}))
	select {}
}


================================================
FILE: conf/core.go
================================================
package conf

type CoreConfig struct {
	Type       string      `yaml:"Type"`
	XrayConfig *XrayConfig `yaml:"XrayConfig"`
}

type XrayConfig struct {
	LogConfig          *LogConfig        `yaml:"Log"`
	AssetPath          string            `yaml:"AssetPath"`
	DnsConfigPath      string            `yaml:"DnsConfigPath"`
	RouteConfigPath    string            `yaml:"RouteConfigPath"`
	ConnectionConfig   *ConnectionConfig `yaml:"ConnectionConfig"`
	InboundConfigPath  string            `yaml:"InboundConfigPath"`
	OutboundConfigPath string            `yaml:"OutboundConfigPath"`
}

type ConnectionConfig struct {
	Handshake    uint32 `yaml:"handshake"`
	ConnIdle     uint32 `yaml:"connIdle"`
	UplinkOnly   uint32 `yaml:"uplinkOnly"`
	DownlinkOnly uint32 `yaml:"downlinkOnly"`
	BufferSize   int32  `yaml:"bufferSize"`
}

func NewConnectionConfig() *ConnectionConfig {
	return &ConnectionConfig{
		Handshake:    4,
		ConnIdle:     30,
		UplinkOnly:   2,
		DownlinkOnly: 4,
		BufferSize:   64,
	}
}


================================================
FILE: conf/log.go
================================================
package conf

type LogConfig struct {
	Level      string `yaml:"Level"`
	AccessPath string `yaml:"AccessPath"`
	ErrorPath  string `yaml:"ErrorPath"`
}

func NewLogConfig() *LogConfig {
	return &LogConfig{
		Level:      "warning",
		AccessPath: "",
		ErrorPath:  "",
	}
}


================================================
FILE: conf/node.go
================================================
package conf

type NodeConfig struct {
	ApiConfig        *ApiConfig        `yaml:"ApiConfig"`
	ControllerConfig *ControllerConfig `yaml:"ControllerConfig"`
}

type ApiConfig struct {
	APIHost      string `yaml:"ApiHost"`
	NodeID       int    `yaml:"NodeID"`
	Key          string `yaml:"ApiKey"`
	NodeType     string `yaml:"NodeType"`
	Timeout      int    `yaml:"Timeout"`
	RuleListPath string `yaml:"RuleListPath"`
}

type ControllerConfig struct {
	ListenIP    string      `yaml:"ListenIP"`
	SendIP      string      `yaml:"SendIP"`
	XrayOptions XrayOptions `yaml:"XrayOptions"`
	HyOptions   HyOptions   `yaml:"HyOptions"`
	LimitConfig LimitConfig `yaml:"LimitConfig"`
	CertConfig  *CertConfig `yaml:"CertConfig"`
}

type RealityConfig struct {
	Dest         interface{} `yaml:"Dest" json:"Dest"`
	Xver         uint64      `yaml:"Xver" json:"Xver"`
	ServerNames  []string    `yaml:"ServerNames" json:"ServerNames"`
	PrivateKey   string      `yaml:"PrivateKey" json:"PrivateKey"`
	MinClientVer string      `yaml:"MinClientVer" json:"MinClientVer"`
	MaxClientVer string      `yaml:"MaxClientVer" json:"MaxClientVer"`
	MaxTimeDiff  uint64      `yaml:"MaxTimeDiff" json:"MaxTimeDiff"`
	ShortIds     []string    `yaml:"ShortIds" json:"ShortIds"`
}

type XrayOptions struct {
	EnableProxyProtocol bool             `yaml:"EnableProxyProtocol"`
	EnableDNS           bool             `yaml:"EnableDNS"`
	DNSType             string           `yaml:"DNSType"`
	EnableUot           bool             `yaml:"EnableUot"`
	EnableTFO           bool             `yaml:"EnableTFO"`
	DisableIVCheck      bool             `yaml:"DisableIVCheck"`
	DisableSniffing     bool             `yaml:"DisableSniffing"`
	EnableFallback      bool             `yaml:"EnableFallback"`
	FallBackConfigs     []FallBackConfig `yaml:"FallBackConfigs"`
}

type HyOptions struct {
	Resolver          string `yaml:"Resolver"`
	ResolvePreference string `yaml:"ResolvePreference"`
	SendDevice        string `yaml:"SendDevice"`
}

type LimitConfig struct {
	EnableRealtime          bool                     `yaml:"EnableRealtime"`
	SpeedLimit              int                      `yaml:"SpeedLimit"`
	IPLimit                 int                      `yaml:"DeviceLimit"`
	ConnLimit               int                      `yaml:"ConnLimit"`
	EnableIpRecorder        bool                     `yaml:"EnableIpRecorder"`
	IpRecorderConfig        *IpReportConfig          `yaml:"IpRecorderConfig"`
	EnableDynamicSpeedLimit bool                     `yaml:"EnableDynamicSpeedLimit"`
	DynamicSpeedLimitConfig *DynamicSpeedLimitConfig `yaml:"DynamicSpeedLimitConfig"`
}

type FallBackConfig struct {
	SNI              string `yaml:"SNI"`
	Alpn             string `yaml:"Alpn"`
	Path             string `yaml:"Path"`
	Dest             string `yaml:"Dest"`
	ProxyProtocolVer uint64 `yaml:"ProxyProtocolVer"`
}

type RecorderConfig struct {
	Url     string `yaml:"Url"`
	Token   string `yaml:"Token"`
	Timeout int    `yaml:"Timeout"`
}

type RedisConfig struct {
	Address  string `yaml:"Address"`
	Password string `yaml:"Password"`
	Db       int    `yaml:"Db"`
	Expiry   int    `json:"Expiry"`
}

type IpReportConfig struct {
	Periodic       int             `yaml:"Periodic"`
	Type           string          `yaml:"Type"`
	RecorderConfig *RecorderConfig `yaml:"RecorderConfig"`
	RedisConfig    *RedisConfig    `yaml:"RedisConfig"`
	EnableIpSync   bool            `yaml:"EnableIpSync"`
}

type DynamicSpeedLimitConfig struct {
	Periodic   int   `yaml:"Periodic"`
	Traffic    int64 `yaml:"Traffic"`
	SpeedLimit int   `yaml:"SpeedLimit"`
	ExpireTime int   `yaml:"ExpireTime"`
}

type CertConfig struct {
	CertMode         string            `yaml:"CertMode"` // none, file, http, dns
	RejectUnknownSni bool              `yaml:"RejectUnknownSni"`
	CertDomain       string            `yaml:"CertDomain"`
	CertFile         string            `yaml:"CertFile"`
	KeyFile          string            `yaml:"KeyFile"`
	Provider         string            `yaml:"Provider"` // alidns, cloudflare, gandi, godaddy....
	Email            string            `yaml:"Email"`
	DNSEnv           map[string]string `yaml:"DNSEnv"`
	RealityConfig    *RealityConfig    `yaml:"RealityConfig"`
}


================================================
FILE: conf/old.go
================================================
package conf

import "log"

type OldConfig struct {
	NodesConfig []*struct {
		ApiConfig        *OldApiConfig        `yaml:"ApiConfig"`
		ControllerConfig *OldControllerConfig `yaml:"ControllerConfig"`
	} `yaml:"Nodes"`
}

type OldControllerConfig struct {
	ListenIP                string                   `yaml:"ListenIP"`
	SendIP                  string                   `yaml:"SendIP"`
	EnableDNS               bool                     `yaml:"EnableDNS"`
	DNSType                 string                   `yaml:"DNSType"`
	DisableUploadTraffic    bool                     `yaml:"DisableUploadTraffic"`
	DisableGetRule          bool                     `yaml:"DisableGetRule"`
	EnableProxyProtocol     bool                     `yaml:"EnableProxyProtocol"`
	EnableFallback          bool                     `yaml:"EnableFallback"`
	DisableIVCheck          bool                     `yaml:"DisableIVCheck"`
	DisableSniffing         bool                     `yaml:"DisableSniffing"`
	FallBackConfigs         []*FallBackConfig        `yaml:"FallBackConfigs"`
	EnableIpRecorder        bool                     `yaml:"EnableIpRecorder"`
	IpRecorderConfig        *IpReportConfig          `yaml:"IpRecorderConfig"`
	EnableDynamicSpeedLimit bool                     `yaml:"EnableDynamicSpeedLimit"`
	DynamicSpeedLimitConfig *DynamicSpeedLimitConfig `yaml:"DynamicSpeedLimitConfig"`
	CertConfig              *CertConfig              `yaml:"CertConfig"`
}

type OldApiConfig struct {
	APIHost             string `yaml:"ApiHost"`
	NodeID              int    `yaml:"NodeID"`
	Key                 string `yaml:"ApiKey"`
	NodeType            string `yaml:"NodeType"`
	EnableVless         bool   `yaml:"EnableVless"`
	Timeout             int    `yaml:"Timeout"`
	SpeedLimit          int    `yaml:"SpeedLimit"`
	DeviceLimit         int    `yaml:"DeviceLimit"`
	RuleListPath        string `yaml:"RuleListPath"`
	DisableCustomConfig bool   `yaml:"DisableCustomConfig"`
}

func migrateOldConfig(c *Conf, old *OldConfig) {
	changed := false
	for i, n := range c.NodesConfig {
		if i >= len(old.NodesConfig) {
			break
		}
		// limit config
		if old.NodesConfig[i].ApiConfig.SpeedLimit != 0 {
			n.ControllerConfig.LimitConfig.SpeedLimit = old.NodesConfig[i].ApiConfig.SpeedLimit
			changed = true
		}
		if old.NodesConfig[i].ApiConfig.DeviceLimit != 0 {
			n.ControllerConfig.LimitConfig.IPLimit = old.NodesConfig[i].ApiConfig.DeviceLimit
			changed = true
		}
		if old.NodesConfig[i].ControllerConfig.EnableDynamicSpeedLimit {
			n.ControllerConfig.LimitConfig.EnableDynamicSpeedLimit = true
			changed = true
		}
		if old.NodesConfig[i].ControllerConfig.DynamicSpeedLimitConfig != nil {
			n.ControllerConfig.LimitConfig.DynamicSpeedLimitConfig =
				old.NodesConfig[i].ControllerConfig.DynamicSpeedLimitConfig
			changed = true
		}
		if old.NodesConfig[i].ControllerConfig.EnableIpRecorder {
			n.ControllerConfig.LimitConfig.EnableIpRecorder = true
			changed = true
		}
		if old.NodesConfig[i].ControllerConfig.IpRecorderConfig != nil {
			n.ControllerConfig.LimitConfig.IpRecorderConfig =
				old.NodesConfig[i].ControllerConfig.IpRecorderConfig
			changed = true
		}
	}
	if changed {
		log.Println("Warning: This config file is old.")
	}
}


================================================
FILE: conf/watch.go
================================================
package conf

import (
	"fmt"
	"github.com/fsnotify/fsnotify"
	"log"
	"path"
	"time"
)

func (p *Conf) Watch(filePath, dnsPath string, reload func()) error {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return fmt.Errorf("new watcher error: %s", err)
	}
	go func() {
		var pre time.Time
		defer watcher.Close()
		for {
			select {
			case e := <-watcher.Events:
				if e.Has(fsnotify.Chmod) {
					continue
				}
				if pre.Add(10 * time.Second).After(time.Now()) {
					continue
				}
				pre = time.Now()
				go func() {
					time.Sleep(10 * time.Second)
					if e.Name == dnsPath {
						log.Println("DNS file changed, reloading...")
					} else {
						log.Println("config dir changed, reloading...")
					}
					*p = *New()
					err := p.LoadFromPath(filePath)
					if err != nil {
						log.Printf("reload config error: %s", err)
					}
					reload()
					log.Println("reload config success")
				}()
			case err := <-watcher.Errors:
				if err != nil {
					log.Printf("File watcher error: %s", err)
				}
			}
		}
	}()
	err = watcher.Add(path.Dir(filePath))
	if err != nil {
		return fmt.Errorf("watch file error: %s", err)
	}
	if dnsPath != "" {
		err = watcher.Add(path.Dir(dnsPath))
		if err != nil {
			return fmt.Errorf("watch dns file error: %s", err)
		}
	}
	return nil
}


================================================
FILE: core/core.go
================================================
package core

import (
	"errors"
	"strings"

	"github.com/Yuzuki616/V2bX/conf"
)

var (
	cores = map[string]func(c *conf.CoreConfig) (Core, error){}
)

func NewCore(c *conf.CoreConfig) (Core, error) {
	// multi core
	if types := strings.Split(c.Type, " "); len(types) > 1 {
		var cs []Core
		for _, t := range types {
			f, ok := cores[strings.ToLower(t)]
			if !ok {
				return nil, errors.New("unknown core type: " + t)
			}
			core1, err := f(c)
			if err != nil {
				return nil, err
			}
			cs = append(cs, core1)
		}
		return &Selector{
			cores: cs,
		}, nil
	}
	// one core
	if f, ok := cores[strings.ToLower(c.Type)]; ok {
		return f(c)
	} else {
		return nil, errors.New("unknown core type")
	}
}

func RegisterCore(t string, f func(c *conf.CoreConfig) (Core, error)) {
	cores[t] = f
}

func RegisteredCore() []string {
	cs := make([]string, 0, len(cores))
	for k := range cores {
		cs = append(cs, k)
	}
	return cs
}


================================================
FILE: core/hy/config.go
================================================
package hy

const (
	mbpsToBps   = 125000
	minSpeedBPS = 16384

	DefaultALPN = "hysteria"

	DefaultStreamReceiveWindow     = 16777216                           // 16 MB
	DefaultConnectionReceiveWindow = DefaultStreamReceiveWindow * 5 / 2 // 40 MB

	DefaultMaxIncomingStreams = 1024

	DefaultMMDBFilename = "GeoLite2-Country.mmdb"

	ServerMaxIdleTimeoutSec     = 60
	DefaultClientIdleTimeoutSec = 20

	DefaultClientHopIntervalSec = 10
)

func SpeedTrans(upM, downM int) (uint64, uint64) {
	up := uint64(upM) * mbpsToBps
	down := uint64(downM) * mbpsToBps
	return up, down
}


================================================
FILE: core/hy/counter.go
================================================
package hy

import (
	"sync"
	"sync/atomic"
)

type UserTrafficCounter struct {
	counters map[string]*counters
	lock     sync.RWMutex
}

type counters struct {
	UpCounter   atomic.Int64
	DownCounter atomic.Int64
	//ConnGauge   atomic.Int64
}

func NewUserTrafficCounter() *UserTrafficCounter {
	return &UserTrafficCounter{
		counters: map[string]*counters{},
	}
}

func (c *UserTrafficCounter) getCounters(auth string) *counters {
	c.lock.RLock()
	cts, ok := c.counters[auth]
	c.lock.RUnlock()
	if !ok {
		cts = &counters{}
		c.counters[auth] = cts
	}
	return cts
}

func (c *UserTrafficCounter) Rx(auth string, n int) {
	cts := c.getCounters(auth)
	cts.DownCounter.Add(int64(n))
}

func (c *UserTrafficCounter) Tx(auth string, n int) {
	cts := c.getCounters(auth)
	cts.UpCounter.Add(int64(n))
}

func (c *UserTrafficCounter) IncConn(_ string) {
	/*cts := c.getCounters(auth)
	cts.ConnGauge.Add(1)*/
	return
}

func (c *UserTrafficCounter) DecConn(_ string) {
	/*cts := c.getCounters(auth)
	cts.ConnGauge.Add(1)*/
	return
}

func (c *UserTrafficCounter) Reset(auth string) {
	cts := c.getCounters(auth)
	cts.UpCounter.Store(0)
	cts.DownCounter.Store(0)
}

func (c *UserTrafficCounter) Delete(auth string) {
	c.lock.Lock()
	delete(c.counters, auth)
	c.lock.Unlock()
}


================================================
FILE: core/hy/counter_test.go
================================================
package hy

import "testing"

func TestUserTrafficCounter_Rx(t *testing.T) {

}


================================================
FILE: core/hy/hy.go
================================================
package hy

import (
	"fmt"
	"sync"

	"github.com/Yuzuki616/V2bX/conf"
	vCore "github.com/Yuzuki616/V2bX/core"
	"github.com/hashicorp/go-multierror"
)

func init() {
	vCore.RegisterCore("hy", NewHy)
}

type Hy struct {
	servers sync.Map
}

func NewHy(_ *conf.CoreConfig) (vCore.Core, error) {
	return &Hy{
		servers: sync.Map{},
	}, nil
}

func (h *Hy) Start() error {
	return nil
}

func (h *Hy) Close() error {
	var errs error
	h.servers.Range(func(tag, s any) bool {
		err := s.(*Server).Close()
		if err != nil {
			errs = multierror.Append(errs, fmt.Errorf("close %s error: %s", tag, err))
		}
		return true
	})
	if errs != nil {
		return errs
	}
	return nil
}

func (h *Hy) Protocols() []string {
	return []string{
		"hysteria",
	}
}


================================================
FILE: core/hy/ipmasker.go
================================================
package hy

import (
	"net"
)

type ipMasker struct {
	IPv4Mask net.IPMask
	IPv6Mask net.IPMask
}

// Mask masks an address with the configured CIDR.
// addr can be "host:port" or just host.
func (m *ipMasker) Mask(addr string) string {
	if m.IPv4Mask == nil && m.IPv6Mask == nil {
		return addr
	}

	host, port, err := net.SplitHostPort(addr)
	if err != nil {
		// just host
		host, port = addr, ""
	}
	ip := net.ParseIP(host)
	if ip == nil {
		// not an IP address, return as is
		return addr
	}
	if ip4 := ip.To4(); ip4 != nil && m.IPv4Mask != nil {
		// IPv4
		host = ip4.Mask(m.IPv4Mask).String()
	} else if ip6 := ip.To16(); ip6 != nil && m.IPv6Mask != nil {
		// IPv6
		host = ip6.Mask(m.IPv6Mask).String()
	}
	if port != "" {
		return net.JoinHostPort(host, port)
	} else {
		return host
	}
}

var defaultIPMasker = &ipMasker{}


================================================
FILE: core/hy/kploader.go
================================================
package hy

import (
	"crypto/tls"
	"sync"

	"github.com/fsnotify/fsnotify"
	"github.com/sirupsen/logrus"
)

type keypairLoader struct {
	certMu   sync.RWMutex
	cert     *tls.Certificate
	certPath string
	keyPath  string
}

func newKeypairLoader(certPath, keyPath string) (*keypairLoader, error) {
	loader := &keypairLoader{
		certPath: certPath,
		keyPath:  keyPath,
	}
	cert, err := tls.LoadX509KeyPair(certPath, keyPath)
	if err != nil {
		return nil, err
	}
	loader.cert = &cert
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	go func() {
		for {
			select {
			case event, ok := <-watcher.Events:
				if !ok {
					return
				}
				switch event.Op {
				case fsnotify.Create, fsnotify.Write, fsnotify.Rename, fsnotify.Chmod:
					logrus.WithFields(logrus.Fields{
						"file": event.Name,
					}).Info("Keypair change detected, reloading...")
					if err := loader.load(); err != nil {
						logrus.WithFields(logrus.Fields{
							"error": err,
						}).Error("Failed to reload keypair")
					} else {
						logrus.Info("Keypair successfully reloaded")
					}
				case fsnotify.Remove:
					_ = watcher.Add(event.Name) // Workaround for vim
					// https://github.com/fsnotify/fsnotify/issues/92
				}
			case err, ok := <-watcher.Errors:
				if !ok {
					return
				}
				logrus.WithFields(logrus.Fields{
					"error": err,
				}).Error("Failed to watch keypair files for changes")
			}
		}
	}()
	err = watcher.Add(certPath)
	if err != nil {
		_ = watcher.Close()
		return nil, err
	}
	err = watcher.Add(keyPath)
	if err != nil {
		_ = watcher.Close()
		return nil, err
	}
	return loader, nil
}

func (kpr *keypairLoader) load() error {
	cert, err := tls.LoadX509KeyPair(kpr.certPath, kpr.keyPath)
	if err != nil {
		return err
	}
	kpr.certMu.Lock()
	kpr.cert = &cert
	kpr.certMu.Unlock()
	return nil
}

func (kpr *keypairLoader) GetCertificateFunc() func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
	return func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
		kpr.certMu.RLock()
		defer kpr.certMu.RUnlock()
		return kpr.cert, nil
	}
}


================================================
FILE: core/hy/mmdb.go
================================================
package hy

import (
	"os"

	"github.com/oschwald/geoip2-golang"
	"github.com/sirupsen/logrus"
)

func loadMMDBReader(filename string) (*geoip2.Reader, error) {
	if _, err := os.Stat(filename); err != nil {
		if os.IsNotExist(err) {
			logrus.Info("GeoLite2 database not found, downloading...")
			logrus.WithFields(logrus.Fields{
				"file": filename,
			}).Info("GeoLite2 database downloaded")
			return geoip2.Open(filename)
		} else {
			// some other error
			return nil, err
		}
	} else {
		// file exists, just open it
		return geoip2.Open(filename)
	}
}


================================================
FILE: core/hy/node.go
================================================
package hy

import (
	"errors"
	"fmt"
	"github.com/Yuzuki616/V2bX/api/panel"
	"github.com/Yuzuki616/V2bX/conf"
	"github.com/Yuzuki616/V2bX/limiter"
)

func (h *Hy) AddNode(tag string, info *panel.NodeInfo, c *conf.ControllerConfig) error {
	if info.Type != "hysteria" {
		return errors.New("the core not support " + info.Type)
	}
	switch c.CertConfig.CertMode {
	case "reality", "none", "":
		return errors.New("hysteria need normal tls cert")
	}
	l, err := limiter.GetLimiter(tag)
	if err != nil {
		return fmt.Errorf("get limiter error: %s", err)
	}
	s := NewServer(tag, l)
	err = s.runServer(info, c)
	if err != nil {
		return fmt.Errorf("run hy server error: %s", err)
	}
	h.servers.Store(tag, s)
	return nil
}

func (h *Hy) DelNode(tag string) error {
	if s, e := h.servers.Load(tag); e {
		err := s.(*Server).Close()
		if err != nil {
			return err
		}
		h.servers.Delete(tag)
		return nil
	}
	return errors.New("the node is not have")
}


================================================
FILE: core/hy/resolver.go
================================================
package hy

import (
	"crypto/tls"
	"errors"
	"github.com/Yuzuki616/hysteria/core/utils"
	rdns "github.com/folbricht/routedns"
	"net"
	"net/url"
	"strings"
)

var errInvalidSyntax = errors.New("invalid syntax")

func setResolver(dns string) error {
	if net.ParseIP(dns) != nil {
		// Just an IP address, treat as UDP 53
		dns = "udp://" + net.JoinHostPort(dns, "53")
	}
	var r rdns.Resolver
	if strings.HasPrefix(dns, "udp://") {
		// Standard UDP DNS resolver
		dns = strings.TrimPrefix(dns, "udp://")
		if dns == "" {
			return errInvalidSyntax
		}
		if _, _, err := utils.SplitHostPort(dns); err != nil {
			// Append the default DNS port
			dns = net.JoinHostPort(dns, "53")
		}
		client, err := rdns.NewDNSClient("dns-udp", dns, "udp", rdns.DNSClientOptions{})
		if err != nil {
			return err
		}
		r = client
	} else if strings.HasPrefix(dns, "tcp://") {
		// Standard TCP DNS resolver
		dns = strings.TrimPrefix(dns, "tcp://")
		if dns == "" {
			return errInvalidSyntax
		}
		if _, _, err := utils.SplitHostPort(dns); err != nil {
			// Append the default DNS port
			dns = net.JoinHostPort(dns, "53")
		}
		client, err := rdns.NewDNSClient("dns-tcp", dns, "tcp", rdns.DNSClientOptions{})
		if err != nil {
			return err
		}
		r = client
	} else if strings.HasPrefix(dns, "https://") {
		// DoH resolver
		if dohURL, err := url.Parse(dns); err != nil {
			return err
		} else {
			// Need to set bootstrap address to avoid loopback DNS lookup
			dohIPAddr, err := net.ResolveIPAddr("ip", dohURL.Hostname())
			if err != nil {
				return err
			}
			client, err := rdns.NewDoHClient("doh", dns, rdns.DoHClientOptions{
				BootstrapAddr: dohIPAddr.String(),
			})
			if err != nil {
				return err
			}
			r = client
		}
	} else if strings.HasPrefix(dns, "tls://") {
		// DoT resolver
		dns = strings.TrimPrefix(dns, "tls://")
		if dns == "" {
			return errInvalidSyntax
		}
		dotHost, _, err := utils.SplitHostPort(dns)
		if err != nil {
			// Append the default DNS port
			dns = net.JoinHostPort(dns, "853")
		}
		// Need to set bootstrap address to avoid loopback DNS lookup
		dotIPAddr, err := net.ResolveIPAddr("ip", dotHost)
		if err != nil {
			return err
		}
		client, err := rdns.NewDoTClient("dot", dns, rdns.DoTClientOptions{
			BootstrapAddr: dotIPAddr.String(),
			TLSConfig:     new(tls.Config),
		})
		if err != nil {
			return err
		}
		r = client
	} else if strings.HasPrefix(dns, "quic://") {
		// DoQ resolver
		dns = strings.TrimPrefix(dns, "quic://")
		if dns == "" {
			return errInvalidSyntax
		}
		doqHost, _, err := utils.SplitHostPort(dns)
		if err != nil {
			// Append the default DNS port
			dns = net.JoinHostPort(dns, "853")
		}
		// Need to set bootstrap address to avoid loopback DNS lookup
		doqIPAddr, err := net.ResolveIPAddr("ip", doqHost)
		if err != nil {
			return err
		}
		client, err := rdns.NewDoQClient("doq", dns, rdns.DoQClientOptions{
			BootstrapAddr: doqIPAddr.String(),
		})
		if err != nil {
			return err
		}
		r = client
	} else {
		return errInvalidSyntax
	}
	cache := rdns.NewCache("cache", r, rdns.CacheOptions{})
	net.DefaultResolver = rdns.NewNetResolver(cache)
	return nil
}


================================================
FILE: core/hy/server.go
================================================
package hy

import (
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/Yuzuki616/V2bX/api/panel"
	"github.com/Yuzuki616/V2bX/conf"
	"github.com/Yuzuki616/V2bX/limiter"
	"github.com/Yuzuki616/hysteria/core/sockopt"
	"github.com/Yuzuki616/quic-go"

	"github.com/Yuzuki616/hysteria/core/acl"
	"github.com/Yuzuki616/hysteria/core/cs"
	"github.com/Yuzuki616/hysteria/core/pktconns"
	"github.com/Yuzuki616/hysteria/core/pmtud"
	"github.com/Yuzuki616/hysteria/core/transport"
	"github.com/sirupsen/logrus"
)

var serverPacketConnFuncFactoryMap = map[string]pktconns.ServerPacketConnFuncFactory{
	"":             pktconns.NewServerUDPConnFunc,
	"udp":          pktconns.NewServerUDPConnFunc,
	"wechat":       pktconns.NewServerWeChatConnFunc,
	"wechat-video": pktconns.NewServerWeChatConnFunc,
	"faketcp":      pktconns.NewServerFakeTCPConnFunc,
}

type Server struct {
	tag     string
	l       *limiter.Limiter
	counter *UserTrafficCounter
	users   sync.Map
	running atomic.Bool
	*cs.Server
}

func NewServer(tag string, l *limiter.Limiter) *Server {
	return &Server{
		tag: tag,
		l:   l,
	}
}

func (s *Server) runServer(node *panel.NodeInfo, c *conf.ControllerConfig) error {
	/*if c.HyOptions == nil {
		return errors.New("hy options is not vail")
	}*/
	// Resolver
	if len(c.HyOptions.Resolver) > 0 {
		err := setResolver(c.HyOptions.Resolver)
		if err != nil {
			return fmt.Errorf("set resolver error: %s", err)
		}
	}
	// tls config
	kpl, err := newKeypairLoader(c.CertConfig.CertFile, c.CertConfig.KeyFile)
	if err != nil {
		return fmt.Errorf("load cert error: %s", err)
	}
	tlsConfig := &tls.Config{
		GetCertificate: kpl.GetCertificateFunc(),
		NextProtos:     []string{DefaultALPN},
		MinVersion:     tls.VersionTLS13,
	}
	// QUIC config
	quicConfig := &quic.Config{
		InitialStreamReceiveWindow:     DefaultStreamReceiveWindow,
		MaxStreamReceiveWindow:         DefaultStreamReceiveWindow,
		InitialConnectionReceiveWindow: DefaultConnectionReceiveWindow,
		MaxConnectionReceiveWindow:     DefaultConnectionReceiveWindow,
		MaxIncomingStreams:             int64(DefaultMaxIncomingStreams),
		MaxIdleTimeout:                 ServerMaxIdleTimeoutSec * time.Second,
		KeepAlivePeriod:                0, // Keep alive should solely be client's responsibility
		DisablePathMTUDiscovery:        false,
		EnableDatagrams:                true,
	}
	if !quicConfig.DisablePathMTUDiscovery && pmtud.DisablePathMTUDiscovery {
		logrus.Info("Path MTU Discovery is not yet supported on this platform")
	}
	// Resolve preference
	if len(c.HyOptions.ResolvePreference) > 0 {
		pref, err := transport.ResolvePreferenceFromString(c.HyOptions.Resolver)
		if err != nil {
			logrus.WithFields(logrus.Fields{
				"error": err,
			}).Fatal("Failed to parse the resolve preference")
		}
		transport.DefaultServerTransport.ResolvePreference = pref
	}
	/*// SOCKS5 outbound
	if config.SOCKS5Outbound.Server != "" {
		transport.DefaultServerTransport.SOCKS5Client = transport.NewSOCKS5Client(config.SOCKS5Outbound.Server,
			config.SOCKS5Outbound.User, config.SOCKS5Outbound.Password)
	}*/
	// Bind outbound
	if c.HyOptions.SendDevice != "" {
		iface, err := net.InterfaceByName(c.HyOptions.SendDevice)
		if err != nil {
			logrus.WithFields(logrus.Fields{
				"error": err,
			}).Fatal("Failed to find the interface")
		}
		transport.DefaultServerTransport.LocalUDPIntf = iface
		sockopt.BindDialer(transport.DefaultServerTransport.Dialer, iface)
	}
	if c.SendIP != "" {
		ip := net.ParseIP(c.SendIP)
		if ip == nil {
			logrus.WithFields(logrus.Fields{
				"error": err,
			}).Fatal("Failed to parse the address")
		}
		transport.DefaultServerTransport.Dialer.LocalAddr = &net.TCPAddr{IP: ip}
		transport.DefaultServerTransport.LocalUDPAddr = &net.UDPAddr{IP: ip}
	}
	// ACL
	var aclEngine *acl.Engine
	// Prometheus
	s.counter = NewUserTrafficCounter()
	// Packet conn
	pktConnFuncFactory := serverPacketConnFuncFactoryMap[""]
	if pktConnFuncFactory == nil {
		return fmt.Errorf("unsopport protocol")
	}
	pktConnFunc := pktConnFuncFactory(node.HyObfs)
	addr := fmt.Sprintf("%s:%d", c.ListenIP, node.Port)
	pktConn, err := pktConnFunc(addr)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"error": err,
			"addr":  addr,
		}).Fatal("Failed to listen on the UDP address")
	}
	// Server
	up, down := SpeedTrans(node.UpMbps, node.DownMbps)
	s.Server, err = cs.NewServer(tlsConfig, quicConfig, pktConn,
		transport.DefaultServerTransport, up, down, false, aclEngine,
		s.connectFunc, s.disconnectFunc, tcpRequestFunc, tcpErrorFunc, udpRequestFunc, udpErrorFunc, s.counter)
	if err != nil {
		return fmt.Errorf("new server error: %s", err)
	}
	logrus.WithField("addr", addr).Info("Server up and running")
	go func() {
		s.running.Store(true)
		defer func() {
			s.running.Store(false)
		}()
		err = s.Server.Serve()
		if err != nil {
			logrus.WithField("addr", addr).Errorf("serve error: %s", err)
		}
	}()
	return nil
}

func (s *Server) authByUser(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) {
	if _, r := s.l.CheckLimit(string(auth), addr.String(), false); r {
		return false, "device limited"
	}
	if _, ok := s.users.Load(string(auth)); ok {
		return true, "Done"
	}
	return false, "Failed"
}

func (s *Server) connectFunc(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) {
	s.l.ConnLimiter.AddConnCount(addr.String(), string(auth), false)
	ok, msg := s.authByUser(addr, auth, sSend, sRecv)
	if !ok {
		logrus.WithFields(logrus.Fields{
			"src": defaultIPMasker.Mask(addr.String()),
		}).Info("Authentication failed, client rejected")
		return false, msg
	}
	logrus.WithFields(logrus.Fields{
		"src":  defaultIPMasker.Mask(addr.String()),
		"Uuid": string(auth),
		"Tag":  s.tag,
	}).Info("Client connected")
	return ok, msg
}

func (s *Server) disconnectFunc(addr net.Addr, auth []byte, err error) {
	s.l.ConnLimiter.DelConnCount(addr.String(), string(auth))
	logrus.WithFields(logrus.Fields{
		"src":   defaultIPMasker.Mask(addr.String()),
		"error": err,
	}).Info("Client disconnected")
}

func tcpRequestFunc(addr net.Addr, auth []byte, reqAddr string, action acl.Action, arg string) {
	logrus.WithFields(logrus.Fields{
		"src":    defaultIPMasker.Mask(addr.String()),
		"dst":    defaultIPMasker.Mask(reqAddr),
		"action": actionToString(action, arg),
	}).Debug("TCP request")
}

func tcpErrorFunc(addr net.Addr, auth []byte, reqAddr string, err error) {
	if err != io.EOF {
		logrus.WithFields(logrus.Fields{
			"src":   defaultIPMasker.Mask(addr.String()),
			"dst":   defaultIPMasker.Mask(reqAddr),
			"error": err,
		}).Info("TCP error")
	} else {
		logrus.WithFields(logrus.Fields{
			"src": defaultIPMasker.Mask(addr.String()),
			"dst": defaultIPMasker.Mask(reqAddr),
		}).Debug("TCP EOF")
	}
}

func udpRequestFunc(addr net.Addr, auth []byte, sessionID uint32) {
	logrus.WithFields(logrus.Fields{
		"src":     defaultIPMasker.Mask(addr.String()),
		"session": sessionID,
	}).Debug("UDP request")
}

func udpErrorFunc(addr net.Addr, auth []byte, sessionID uint32, err error) {
	if err != io.EOF {
		logrus.WithFields(logrus.Fields{
			"src":     defaultIPMasker.Mask(addr.String()),
			"session": sessionID,
			"error":   err,
		}).Info("UDP error")
	} else {
		logrus.WithFields(logrus.Fields{
			"src":     defaultIPMasker.Mask(addr.String()),
			"session": sessionID,
		}).Debug("UDP EOF")
	}
}

func actionToString(action acl.Action, arg string) string {
	switch action {
	case acl.ActionDirect:
		return "Direct"
	case acl.ActionProxy:
		return "Proxy"
	case acl.ActionBlock:
		return "Block"
	case acl.ActionHijack:
		return "Hijack to " + arg
	default:
		return "Unknown"
	}
}


================================================
FILE: core/hy/server_test.go
================================================
package hy

import (
	"encoding/base64"
	"github.com/Yuzuki616/V2bX/api/panel"
	"github.com/Yuzuki616/V2bX/conf"
	"github.com/Yuzuki616/V2bX/limiter"
	"github.com/sirupsen/logrus"
	"log"
	"testing"
	"time"
)

func TestServer(t *testing.T) {
	logrus.SetLevel(logrus.DebugLevel)
	limiter.Init()
	l := limiter.AddLimiter("test", &conf.LimitConfig{}, nil)
	s := NewServer("test", l)
	t.Log(s.runServer(&panel.NodeInfo{
		Port:     1145,
		UpMbps:   100,
		DownMbps: 100,
		HyObfs:   "atresssdaaaadd",
	}, &conf.ControllerConfig{
		ListenIP:  "127.0.0.1",
		HyOptions: conf.HyOptions{},
		CertConfig: &conf.CertConfig{
			CertFile: "../../test_data/1.pem",
			KeyFile:  "../../test_data/1.key",
		},
	}))
	s.users.Store("test1111", struct{}{})
	go func() {
		for {
			time.Sleep(10 * time.Second)
			auth := base64.StdEncoding.EncodeToString([]byte("test1111"))
			log.Println(auth)
			log.Println(s.counter.getCounters(auth).UpCounter.Load())
		}
	}()
	select {}
}


================================================
FILE: core/hy/user.go
================================================
package hy

import (
	"encoding/base64"
	"errors"
	"github.com/Yuzuki616/V2bX/api/panel"
	"github.com/Yuzuki616/V2bX/core"
)

func (h *Hy) AddUsers(p *core.AddUsersParams) (int, error) {
	s, ok := h.servers.Load(p.Tag)
	if !ok {
		return 0, errors.New("the node not have")
	}
	u := &s.(*Server).users
	for i := range p.UserInfo {
		u.Store(p.UserInfo[i].Uuid, struct{}{})
	}
	return len(p.UserInfo), nil
}

func (h *Hy) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) {
	v, _ := h.servers.Load(tag)
	s := v.(*Server)
	auth := base64.StdEncoding.EncodeToString([]byte(uuid))
	up = s.counter.getCounters(auth).UpCounter.Load()
	down = s.counter.getCounters(auth).DownCounter.Load()
	if reset {
		s.counter.Reset(auth)
	}
	return
}

func (h *Hy) DelUsers(users []panel.UserInfo, tag string) error {
	v, e := h.servers.Load(tag)
	if !e {
		return errors.New("the node is not have")
	}
	s := v.(*Server)
	for i := range users {
		s.users.Delete(users[i].Uuid)
		s.counter.Delete(base64.StdEncoding.EncodeToString([]byte(users[i].Uuid)))
	}
	return nil
}


================================================
FILE: core/imports/hy.go
================================================
//go:build hy

package imports

// not yet tested
import _ "github.com/Yuzuki616/V2bX/core/hy"


================================================
FILE: core/imports/imports.go
================================================
package imports


================================================
FILE: core/imports/xray.go
================================================
//go:build xray

package imports

import _ "github.com/Yuzuki616/V2bX/core/xray"


================================================
FILE: core/interface.go
================================================
package core

import (
	"github.com/Yuzuki616/V2bX/api/panel"
	"github.com/Yuzuki616/V2bX/conf"
)

type AddUsersParams struct {
	Tag      string
	Config   *conf.ControllerConfig
	UserInfo []panel.UserInfo
	NodeInfo *panel.NodeInfo
}
type Core interface {
	Start() error
	Close() error
	AddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error
	DelNode(tag string) error
	AddUsers(p *AddUsersParams) (added int, err error)
	GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64)
	DelUsers(users []panel.UserInfo, tag string) error
	Protocols() []string
}


================================================
FILE: core/selector.go
================================================
package core

import (
	"errors"
	"sync"

	"github.com/Yuzuki616/V2bX/api/panel"
	"github.com/Yuzuki616/V2bX/conf"
	"github.com/hashicorp/go-multierror"
)

type Selector struct {
	cores []Core
	nodes sync.Map
}

func (s *Selector) Start() error {
	for i := range s.cores {
		err := s.cores[i].Start()
		return err
	}
	return nil
}

func (s *Selector) Close() error {
	var errs error
	for i := range s.cores {
		errs = multierror.Append(errs, s.cores[i].Close())
	}
	return errs
}

func isSupported(protocol string, protocols []string) bool {
	for i := range protocols {
		if protocol == protocols[i] {
			return true
		}
	}
	return false
}

func (s *Selector) AddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error {
	for i := range s.cores {
		if !isSupported(info.Type, s.cores[i].Protocols()) {
			continue
		}
		err := s.cores[i].AddNode(tag, info, config)
		if err != nil {
			return err
		}
		s.nodes.Store(tag, i)
		return nil
	}
	return errors.New("the node type is not support")
}

func (s *Selector) DelNode(tag string) error {
	if t, e := s.nodes.Load(tag); e {
		err := s.cores[t.(int)].DelNode(tag)
		if err != nil {
			return err
		}
		s.nodes.Delete(tag)
		return nil
	}
	return errors.New("the node is not have")
}

func (s *Selector) AddUsers(p *AddUsersParams) (added int, err error) {
	t, e := s.nodes.Load(p.Tag)
	if !e {
		return 0, errors.New("the node is not have")
	}
	return s.cores[t.(int)].AddUsers(p)
}

func (s *Selector) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) {
	t, e := s.nodes.Load(tag)
	if !e {
		return 0, 0
	}
	return s.cores[t.(int)].GetUserTraffic(tag, uuid, reset)
}

func (s *Selector) DelUsers(users []panel.UserInfo, tag string) error {
	t, e := s.nodes.Load(tag)
	if !e {
		return errors.New("the node is not have")
	}
	return s.cores[t.(int)].DelUsers(users, tag)
}

func (s *Selector) Protocols() []string {
	protocols := make([]string, 0)
	for i := range s.cores {
		protocols = append(protocols, s.cores[i].Protocols()...)
	}
	return protocols
}


================================================
FILE: core/xray/app/app.go
================================================
// Package app contains the third-party app used to replace the default app in xray-core
package app


================================================
FILE: core/xray/app/dispatcher/config.pb.go
================================================
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.28.1
// 	protoc        v3.21.12
// source: config.proto

package dispatcher

import (
	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
	reflect "reflect"
	sync "sync"
)

const (
	// Verify that this generated code is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
	// Verify that runtime/protoimpl is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

type SessionConfig struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields
}

func (x *SessionConfig) Reset() {
	*x = SessionConfig{}
	if protoimpl.UnsafeEnabled {
		mi := &file_config_proto_msgTypes[0]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *SessionConfig) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*SessionConfig) ProtoMessage() {}

func (x *SessionConfig) ProtoReflect() protoreflect.Message {
	mi := &file_config_proto_msgTypes[0]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use SessionConfig.ProtoReflect.Descriptor instead.
func (*SessionConfig) Descriptor() ([]byte, []int) {
	return file_config_proto_rawDescGZIP(), []int{0}
}

type Config struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Settings *SessionConfig `protobuf:"bytes,1,opt,name=settings,proto3" json:"settings,omitempty"`
}

func (x *Config) Reset() {
	*x = Config{}
	if protoimpl.UnsafeEnabled {
		mi := &file_config_proto_msgTypes[1]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *Config) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*Config) ProtoMessage() {}

func (x *Config) ProtoReflect() protoreflect.Message {
	mi := &file_config_proto_msgTypes[1]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
	return file_config_proto_rawDescGZIP(), []int{1}
}

func (x *Config) GetSettings() *SessionConfig {
	if x != nil {
		return x.Settings
	}
	return nil
}

var File_config_proto protoreflect.FileDescriptor

var file_config_proto_rawDesc = []byte{
	0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x18,
	0x76, 0x32, 0x62, 0x78, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x69,
	0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x22, 0x15, 0x0a, 0x0d, 0x53, 0x65, 0x73, 0x73,
	0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22,
	0x4d, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x43, 0x0a, 0x08, 0x73, 0x65, 0x74,
	0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x76, 0x32,
	0x62, 0x78, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x69, 0x73, 0x70,
	0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f,
	0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x6a,
	0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x62, 0x78, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
	0x61, 0x70, 0x70, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x50, 0x01,
	0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x59, 0x75, 0x7a,
	0x75, 0x6b, 0x69, 0x36, 0x31, 0x36, 0x2f, 0x56, 0x32, 0x62, 0x58, 0x2f, 0x63, 0x6f, 0x72, 0x65,
	0x2f, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0xaa,
	0x02, 0x18, 0x56, 0x32, 0x62, 0x58, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e,
	0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
	0x6f, 0x33,
}

var (
	file_config_proto_rawDescOnce sync.Once
	file_config_proto_rawDescData = file_config_proto_rawDesc
)

func file_config_proto_rawDescGZIP() []byte {
	file_config_proto_rawDescOnce.Do(func() {
		file_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_config_proto_rawDescData)
	})
	return file_config_proto_rawDescData
}

var file_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_config_proto_goTypes = []interface{}{
	(*SessionConfig)(nil), // 0: v2bx.core.app.dispatcher.SessionConfig
	(*Config)(nil),        // 1: v2bx.core.app.dispatcher.Config
}
var file_config_proto_depIdxs = []int32{
	0, // 0: v2bx.core.app.dispatcher.Config.settings:type_name -> v2bx.core.app.dispatcher.SessionConfig
	1, // [1:1] is the sub-list for method output_type
	1, // [1:1] is the sub-list for method input_type
	1, // [1:1] is the sub-list for extension type_name
	1, // [1:1] is the sub-list for extension extendee
	0, // [0:1] is the sub-list for field type_name
}

func init() { file_config_proto_init() }
func file_config_proto_init() {
	if File_config_proto != nil {
		return
	}
	if !protoimpl.UnsafeEnabled {
		file_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*SessionConfig); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*Config); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
	}
	type x struct{}
	out := protoimpl.TypeBuilder{
		File: protoimpl.DescBuilder{
			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
			RawDescriptor: file_config_proto_rawDesc,
			NumEnums:      0,
			NumMessages:   2,
			NumExtensions: 0,
			NumServices:   0,
		},
		GoTypes:           file_config_proto_goTypes,
		DependencyIndexes: file_config_proto_depIdxs,
		MessageInfos:      file_config_proto_msgTypes,
	}.Build()
	File_config_proto = out.File
	file_config_proto_rawDesc = nil
	file_config_proto_goTypes = nil
	file_config_proto_depIdxs = nil
}


================================================
FILE: core/xray/app/dispatcher/config.proto
================================================
syntax = "proto3";

package v2bx.core.app.dispatcher;
option csharp_namespace = "V2bX.core.app.dispatcher";
option go_package = "github.com/Yuzuki616/V2bX/core/xray/app/dispatcher";
option java_package = "com.v2bx.core.app.dispatcher";
option java_multiple_files = true;

message SessionConfig {
  reserved 1;
}

message Config {
  SessionConfig settings = 1;
}


================================================
FILE: core/xray/app/dispatcher/default.go
================================================
package dispatcher

//go:generate go run github.com/xtls/xray-core/common/errors/errorgen

import (
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/Yuzuki616/V2bX/common/rate"
	"github.com/Yuzuki616/V2bX/limiter"
	routingSession "github.com/xtls/xray-core/features/routing/session"

	"github.com/xtls/xray-core/common"
	"github.com/xtls/xray-core/common/buf"
	"github.com/xtls/xray-core/common/log"
	"github.com/xtls/xray-core/common/net"
	"github.com/xtls/xray-core/common/protocol"
	"github.com/xtls/xray-core/common/session"
	"github.com/xtls/xray-core/core"
	"github.com/xtls/xray-core/features/dns"
	"github.com/xtls/xray-core/features/outbound"
	"github.com/xtls/xray-core/features/policy"
	"github.com/xtls/xray-core/features/routing"
	"github.com/xtls/xray-core/features/stats"
	"github.com/xtls/xray-core/transport"
	"github.com/xtls/xray-core/transport/pipe"
)

var errSniffingTimeout = newError("timeout on sniffing")

type cachedReader struct {
	sync.Mutex
	reader *pipe.Reader
	cache  buf.MultiBuffer
}

func (r *cachedReader) Cache(b *buf.Buffer) {
	mb, _ := r.reader.ReadMultiBufferTimeout(time.Millisecond * 100)
	r.Lock()
	if !mb.IsEmpty() {
		r.cache, _ = buf.MergeMulti(r.cache, mb)
	}
	b.Clear()
	rawBytes := b.Extend(buf.Size)
	n := r.cache.Copy(rawBytes)
	b.Resize(0, int32(n))
	r.Unlock()
}

func (r *cachedReader) readInternal() buf.MultiBuffer {
	r.Lock()
	defer r.Unlock()

	if r.cache != nil && !r.cache.IsEmpty() {
		mb := r.cache
		r.cache = nil
		return mb
	}

	return nil
}

func (r *cachedReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
	mb := r.readInternal()
	if mb != nil {
		return mb, nil
	}

	return r.reader.ReadMultiBuffer()
}

func (r *cachedReader) ReadMultiBufferTimeout(timeout time.Duration) (buf.MultiBuffer, error) {
	mb := r.readInternal()
	if mb != nil {
		return mb, nil
	}

	return r.reader.ReadMultiBufferTimeout(timeout)
}

func (r *cachedReader) Interrupt() {
	r.Lock()
	if r.cache != nil {
		r.cache = buf.ReleaseMulti(r.cache)
	}
	r.Unlock()
	r.reader.Interrupt()
}

// DefaultDispatcher is a default implementation of Dispatcher.
type DefaultDispatcher struct {
	ohm    outbound.Manager
	router routing.Router
	policy policy.Manager
	stats  stats.Manager
	dns    dns.Client
	fdns   dns.FakeDNSEngine
}

func init() {
	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
		d := new(DefaultDispatcher)
		if err := core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dc dns.Client) error {
			core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
				d.fdns = fdns
			})
			return d.Init(config.(*Config), om, router, pm, sm, dc)
		}); err != nil {
			return nil, err
		}
		return d, nil
	}))
}

// Init initializes DefaultDispatcher.
func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dns dns.Client) error {
	d.ohm = om
	d.router = router
	d.policy = pm
	d.stats = sm
	d.dns = dns
	return nil
}

// Type implements common.HasType.
func (*DefaultDispatcher) Type() interface{} {
	return routing.DispatcherType()
}

// Start implements common.Runnable.
func (*DefaultDispatcher) Start() error {
	return nil
}

// Close implements common.Closable.
func (*DefaultDispatcher) Close() error { return nil }

func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sniffing session.SniffingRequest) (*transport.Link, *transport.Link, *limiter.Limiter, error) {
	downOpt := pipe.OptionsFromContext(ctx)
	upOpt := downOpt

	if network == net.Network_UDP {
		var ip2domain *sync.Map // net.IP.String() => domain, this map is used by server side when client turn on fakedns
		// Client will send domain address in the buffer.UDP.Address, server record all possible target IP addrs.
		// When target replies, server will restore the domain and send back to client.
		// Note: this map is not global but per connection context
		upOpt = append(upOpt, pipe.OnTransmission(func(mb buf.MultiBuffer) buf.MultiBuffer {
			for i, buffer := range mb {
				if buffer.UDP == nil {
					continue
				}
				addr := buffer.UDP.Address
				if addr.Family().IsIP() {
					if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && fkr0.IsIPInIPPool(addr) && sniffing.Enabled {
						domain := fkr0.GetDomainFromFakeDNS(addr)
						if len(domain) > 0 {
							buffer.UDP.Address = net.DomainAddress(domain)
							newError("[fakedns client] override with domain: ", domain, " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
						} else {
							newError("[fakedns client] failed to find domain! :", addr.String(), " for xUDP buffer at ", i).AtWarning().WriteToLog(session.ExportIDToError(ctx))
						}
					}
				} else {
					if ip2domain == nil {
						ip2domain = new(sync.Map)
						newError("[fakedns client] create a new map").WriteToLog(session.ExportIDToError(ctx))
					}
					domain := addr.Domain()
					ips, err := d.dns.LookupIP(domain, dns.IPOption{true, true, false})
					if err == nil {
						for _, ip := range ips {
							ip2domain.Store(ip.String(), domain)
						}
						newError("[fakedns client] candidate ip: "+fmt.Sprintf("%v", ips), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
					} else {
						newError("[fakedns client] failed to look up IP for ", domain, " for xUDP buffer at ", i).Base(err).WriteToLog(session.ExportIDToError(ctx))
					}
				}
			}
			return mb
		}))
		downOpt = append(downOpt, pipe.OnTransmission(func(mb buf.MultiBuffer) buf.MultiBuffer {
			for i, buffer := range mb {
				if buffer.UDP == nil {
					continue
				}
				addr := buffer.UDP.Address
				if addr.Family().IsIP() {
					if ip2domain == nil {
						continue
					}
					if domain, found := ip2domain.Load(addr.IP().String()); found {
						buffer.UDP.Address = net.DomainAddress(domain.(string))
						newError("[fakedns client] restore domain: ", domain.(string), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
					}
				} else {
					if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok {
						fakeIp := fkr0.GetFakeIPForDomain(addr.Domain())
						buffer.UDP.Address = fakeIp[0]
						newError("[fakedns client] restore FakeIP: ", buffer.UDP, fmt.Sprintf("%v", fakeIp), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
					}
				}
			}
			return mb
		}))
	}
	uplinkReader, uplinkWriter := pipe.New(upOpt...)
	downlinkReader, downlinkWriter := pipe.New(downOpt...)

	inboundLink := &transport.Link{
		Reader: downlinkReader,
		Writer: uplinkWriter,
	}

	outboundLink := &transport.Link{
		Reader: uplinkReader,
		Writer: downlinkWriter,
	}

	sessionInbound := session.InboundFromContext(ctx)
	var user *protocol.MemoryUser
	if sessionInbound != nil {
		user = sessionInbound.User
	}
	var limit *limiter.Limiter
	if user != nil && len(user.Email) > 0 {
		var err error
		limit, err = limiter.GetLimiter(sessionInbound.Tag)
		if err != nil {
			newError("Get limit info error: ", err).AtError().WriteToLog()
			common.Close(outboundLink.Writer)
			common.Close(inboundLink.Writer)
			common.Interrupt(outboundLink.Reader)
			common.Interrupt(inboundLink.Reader)
			return nil, nil, nil, newError("Get limit info error: ", err)
		}
		// Speed Limit and Device Limit
		w, reject := limit.CheckLimit(user.Email,
			sessionInbound.Source.Address.IP().String(),
			network == net.Network_TCP)
		if reject {
			newError("Limited ", user.Email, " by conn or ip").AtWarning().WriteToLog()
			common.Close(outboundLink.Writer)
			common.Close(inboundLink.Writer)
			common.Interrupt(outboundLink.Reader)
			common.Interrupt(inboundLink.Reader)
			return nil, nil, nil, newError("Limited ", user.Email, " by conn or ip")
		}
		if w != nil {
			inboundLink.Writer = rate.NewRateLimitWriter(inboundLink.Writer, w)
			outboundLink.Writer = rate.NewRateLimitWriter(outboundLink.Writer, w)
		}
		p := d.policy.ForLevel(user.Level)
		if p.Stats.UserUplink {
			name := "user>>>" + user.Email + ">>>traffic>>>uplink"
			if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
				inboundLink.Writer = &SizeStatWriter{
					Counter: c,
					Writer:  inboundLink.Writer,
				}
			}
		}
		if p.Stats.UserDownlink {
			name := "user>>>" + user.Email + ">>>traffic>>>downlink"
			if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
				outboundLink.Writer = &SizeStatWriter{
					Counter: c,
					Writer:  outboundLink.Writer,
				}
			}
		}
	}

	return inboundLink, outboundLink, limit, nil
}

func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool {
	domain := result.Domain()
	if domain == "" {
		return false
	}
	for _, d := range request.ExcludeForDomain {
		if strings.ToLower(domain) == d {
			return false
		}
	}
	protocolString := result.Protocol()
	if resComp, ok := result.(SnifferResultComposite); ok {
		protocolString = resComp.ProtocolForDomainResult()
	}
	for _, p := range request.OverrideDestinationForProtocol {
		if strings.HasPrefix(protocolString, p) {
			return true
		}
		if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" &&
			destination.Address.Family().IsIP() && fkr0.IsIPInIPPool(destination.Address) {
			newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx))
			return true
		}
		if resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok {
			if resultSubset.IsProtoSubsetOf(p) {
				return true
			}
		}
	}

	return false
}

// Dispatch implements routing.Dispatcher.
func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (*transport.Link, error) {
	if !destination.IsValid() {
		panic("Dispatcher: Invalid destination.")
	}
	ob := &session.Outbound{
		Target: destination,
	}
	ctx = session.ContextWithOutbound(ctx, ob)
	content := session.ContentFromContext(ctx)
	if content == nil {
		content = new(session.Content)
		ctx = session.ContextWithContent(ctx, content)
	}
	sniffingRequest := content.SniffingRequest
	inbound, outbound, l, err := d.getLink(ctx, destination.Network, sniffingRequest)
	if err != nil {
		return nil, err
	}
	if !sniffingRequest.Enabled {
		go d.routedDispatch(ctx, outbound, destination, l)
	} else {
		go func() {
			cReader := &cachedReader{
				reader: outbound.Reader.(*pipe.Reader),
			}
			outbound.Reader = cReader
			result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network, l)
			if _, ok := err.(limitedError); ok {
				newError(err).AtInfo().WriteToLog()
				common.Close(outbound.Writer)
				common.Interrupt(outbound.Reader)
				return
			}
			if err == nil {
				content.Protocol = result.Protocol()
			}
			if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
				domain := result.Domain()
				newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
				destination.Address = net.ParseAddress(domain)
				if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
					ob.RouteTarget = destination
				} else {
					ob.Target = destination
				}
			}
			d.routedDispatch(ctx, outbound, destination, l)
		}()
	}
	return inbound, nil
}

// DispatchLink implements routing.Dispatcher.
func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.Destination, outbound *transport.Link) error {
	if !destination.IsValid() {
		return newError("Dispatcher: Invalid destination.")
	}
	ob := &session.Outbound{
		Target: destination,
	}
	ctx = session.ContextWithOutbound(ctx, ob)
	content := session.ContentFromContext(ctx)
	if content == nil {
		content = new(session.Content)
		ctx = session.ContextWithContent(ctx, content)
	}
	sniffingRequest := content.SniffingRequest
	if !sniffingRequest.Enabled {
		go d.routedDispatch(ctx, outbound, destination, nil)
	} else {
		go func() {
			cReader := &cachedReader{
				reader: outbound.Reader.(*pipe.Reader),
			}
			outbound.Reader = cReader
			result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network, nil)
			if _, ok := err.(limitedError); ok {
				newError(err).AtInfo().WriteToLog()
				common.Close(outbound.Writer)
				common.Interrupt(outbound.Reader)
				return
			}
			if err == nil {
				content.Protocol = result.Protocol()
			}
			if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
				domain := result.Domain()
				newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
				destination.Address = net.ParseAddress(domain)
				if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
					ob.RouteTarget = destination
				} else {
					ob.Target = destination
				}
			}
			d.routedDispatch(ctx, outbound, destination, nil)
		}()
	}
	return nil
}

type limitedError string

func (l limitedError) Error() string {
	return string(l)
}

func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network, l *limiter.Limiter) (result SniffResult, err error) {
	payload := buf.New()
	defer payload.Release()

	defer func() {
		if err != nil {
			return
		}
		// Check if domain and protocol hit the rule
		sessionInbound := session.InboundFromContext(ctx)
		// Whether the inbound connection contains a user
		if sessionInbound.User != nil {
			if l == nil {
				l, err = limiter.GetLimiter(sessionInbound.Tag)
				if err != nil {
					return
				}
			}
			if l.CheckDomainRule(result.Domain()) {
				err = limitedError(fmt.Sprintf(
					"User %s access domain %s reject by rule",
					sessionInbound.User.Email,
					result.Domain()))
			}
			if l.CheckProtocolRule(result.Protocol()) {
				err = limitedError(fmt.Sprintf(
					"User %s access protocol %s reject by rule",
					sessionInbound.User.Email,
					result.Protocol()))
			}
		}
	}()

	sniffer := NewSniffer(ctx)
	metaresult, metadataErr := sniffer.SniffMetadata(ctx)
	if metadataOnly {
		return metaresult, metadataErr
	}
	contentResult, contentErr := func() (SniffResult, error) {
		totalAttempt := 0
		for {
			select {
			case <-ctx.Done():
				return nil, ctx.Err()
			default:
				totalAttempt++
				if totalAttempt > 2 {
					return nil, errSniffingTimeout
				}

				cReader.Cache(payload)
				if !payload.IsEmpty() {
					result, err := sniffer.Sniff(ctx, payload.Bytes(), network)
					if err != common.ErrNoClue {
						return result, err
					}
				}
				if payload.IsFull() {
					return nil, errUnknownContent
				}
			}
		}
	}()
	if contentErr != nil && metadataErr == nil {
		return metaresult, nil
	}
	if contentErr == nil && metadataErr == nil {
		return CompositeResult(metaresult, contentResult), nil
	}
	return contentResult, contentErr
}

func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination, l *limiter.Limiter) {
	ob := session.OutboundFromContext(ctx)
	if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() {
		proxied := hosts.LookupHosts(ob.Target.String())
		if proxied != nil {
			ro := ob.RouteTarget == destination
			destination.Address = *proxied
			if ro {
				ob.RouteTarget = destination
			} else {
				ob.Target = destination
			}
		}
	}
	var handler outbound.Handler

	// del connect count
	if l != nil {
		sessionInbound := session.InboundFromContext(ctx)
		if sessionInbound.User != nil {
			if destination.Network == net.Network_TCP {
				defer func() {
					l.ConnLimiter.DelConnCount(sessionInbound.User.Email, sessionInbound.Source.Address.IP().String())
				}()
			}
		}
	}

	routingLink := routingSession.AsRoutingContext(ctx)
	inTag := routingLink.GetInboundTag()
	isPickRoute := 0
	if forcedOutboundTag := session.GetForcedOutboundTagFromContext(ctx); forcedOutboundTag != "" {
		ctx = session.SetForcedOutboundTagToContext(ctx, "")
		if h := d.ohm.GetHandler(forcedOutboundTag); h != nil {
			isPickRoute = 1
			newError("taking platform initialized detour [", forcedOutboundTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
			handler = h
		} else {
			newError("non existing tag for platform initialized detour: ", forcedOutboundTag).AtError().WriteToLog(session.ExportIDToError(ctx))
			common.Close(link.Writer)
			common.Interrupt(link.Reader)
			return
		}
	} else if d.router != nil {
		if route, err := d.router.PickRoute(routingLink); err == nil {
			outTag := route.GetOutboundTag()
			if h := d.ohm.GetHandler(outTag); h != nil {
				isPickRoute = 2
				newError("taking detour [", outTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
				handler = h
			} else {
				newError("non existing outTag: ", outTag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
			}
		} else {
			newError("default route for ", destination).WriteToLog(session.ExportIDToError(ctx))
		}
	}

	if handler == nil {
		handler = d.ohm.GetHandler(inTag) // Default outbound handier tag should be as same as the inbound tag
	}

	// If there is no outbound with tag as same as the inbound tag
	if handler == nil {
		handler = d.ohm.GetDefaultHandler()
	}

	if handler == nil {
		newError("default outbound handler not exist").WriteToLog(session.ExportIDToError(ctx))
		common.Close(link.Writer)
		common.Interrupt(link.Reader)
		return
	}

	if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil {
		if tag := handler.Tag(); tag != "" {
			if inTag == "" {
				accessMessage.Detour = tag
			} else if isPickRoute == 1 {
				accessMessage.Detour = inTag + " ==> " + tag
			} else if isPickRoute == 2 {
				accessMessage.Detour = inTag + " -> " + tag
			} else {
				accessMessage.Detour = inTag + " >> " + tag
			}
		}
		log.Record(accessMessage)
	}

	handler.Dispatch(ctx, link)
}


================================================
FILE: core/xray/app/dispatcher/dispatcher.go
================================================
package dispatcher

//go:generate go run github.com/xtls/xray-core/common/errors/errorgen


================================================
FILE: core/xray/app/dispatcher/errors.generated.go
================================================
package dispatcher

import "github.com/xtls/xray-core/common/errors"

type errPathObjHolder struct{}

func newError(values ...interface{}) *errors.Error {
	return errors.New(values...).WithPathObj(errPathObjHolder{})
}


================================================
FILE: core/xray/app/dispatcher/fakednssniffer.go
================================================
package dispatcher

import (
	"context"
	"strings"

	"github.com/xtls/xray-core/common"
	"github.com/xtls/xray-core/common/net"
	"github.com/xtls/xray-core/common/session"
	"github.com/xtls/xray-core/core"
	"github.com/xtls/xray-core/features/dns"
)

// newFakeDNSSniffer Creates a Fake DNS metadata sniffer
func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error) {
	var fakeDNSEngine dns.FakeDNSEngine
	{
		fakeDNSEngineFeat := core.MustFromContext(ctx).GetFeature((*dns.FakeDNSEngine)(nil))
		if fakeDNSEngineFeat != nil {
			fakeDNSEngine = fakeDNSEngineFeat.(dns.FakeDNSEngine)
		}
	}

	if fakeDNSEngine == nil {
		errNotInit := newError("FakeDNSEngine is not initialized, but such a sniffer is used").AtError()
		return protocolSnifferWithMetadata{}, errNotInit
	}
	return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
		Target := session.OutboundFromContext(ctx).Target
		if Target.Network == net.Network_TCP || Target.Network == net.Network_UDP {
			domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(Target.Address)
			if domainFromFakeDNS != "" {
				newError("fake dns got domain: ", domainFromFakeDNS, " for ip: ", Target.Address.String()).WriteToLog(session.ExportIDToError(ctx))
				return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
			}
		}

		if ipAddressInRangeValueI := ctx.Value(ipAddressInRange); ipAddressInRangeValueI != nil {
			ipAddressInRangeValue := ipAddressInRangeValueI.(*ipAddressInRangeOpt)
			if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok {
				inPool := fkr0.IsIPInIPPool(Target.Address)
				ipAddressInRangeValue.addressInRange = &inPool
			}
		}

		return nil, common.ErrNoClue
	}, metadataSniffer: true}, nil
}

type fakeDNSSniffResult struct {
	domainName string
}

func (fakeDNSSniffResult) Protocol() string {
	return "fakedns"
}

func (f fakeDNSSniffResult) Domain() string {
	return f.domainName
}

type fakeDNSExtraOpts int

const ipAddressInRange fakeDNSExtraOpts = 1

type ipAddressInRangeOpt struct {
	addressInRange *bool
}

type DNSThenOthersSniffResult struct {
	domainName           string
	protocolOriginalName string
}

func (f DNSThenOthersSniffResult) IsProtoSubsetOf(protocolName string) bool {
	return strings.HasPrefix(protocolName, f.protocolOriginalName)
}

func (DNSThenOthersSniffResult) Protocol() string {
	return "fakedns+others"
}

func (f DNSThenOthersSniffResult) Domain() string {
	return f.domainName
}

func newFakeDNSThenOthers(ctx context.Context, fakeDNSSniffer protocolSnifferWithMetadata, others []protocolSnifferWithMetadata) (
	protocolSnifferWithMetadata, error,
) { // nolint: unparam
	// ctx may be used in the future
	_ = ctx
	return protocolSnifferWithMetadata{
		protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
			ipAddressInRangeValue := &ipAddressInRangeOpt{}
			ctx = context.WithValue(ctx, ipAddressInRange, ipAddressInRangeValue)
			result, err := fakeDNSSniffer.protocolSniffer(ctx, bytes)
			if err == nil {
				return result, nil
			}
			if ipAddressInRangeValue.addressInRange != nil {
				if *ipAddressInRangeValue.addressInRange {
					for _, v := range others {
						if v.metadataSniffer || bytes != nil {
							if result, err := v.protocolSniffer(ctx, bytes); err == nil {
								return DNSThenOthersSniffResult{domainName: result.Domain(), protocolOriginalName: result.Protocol()}, nil
							}
						}
					}
					return nil, common.ErrNoClue
				}
				newError("ip address not in fake dns range, return as is").AtDebug().WriteToLog()
				return nil, common.ErrNoClue
			}
			newError("fake dns sniffer did not set address in range option, assume false.").AtWarning().WriteToLog()
			return nil, common.ErrNoClue
		},
		metadataSniffer: false,
	}, nil
}


================================================
FILE: core/xray/app/dispatcher/sniffer.go
================================================
package dispatcher

import (
	"context"

	"github.com/xtls/xray-core/common"
	"github.com/xtls/xray-core/common/net"
	"github.com/xtls/xray-core/common/protocol/bittorrent"
	"github.com/xtls/xray-core/common/protocol/http"
	"github.com/xtls/xray-core/common/protocol/quic"
	"github.com/xtls/xray-core/common/protocol/tls"
)

type SniffResult interface {
	Protocol() string
	Domain() string
}

type protocolSniffer func(context.Context, []byte) (SniffResult, error)

type protocolSnifferWithMetadata struct {
	protocolSniffer protocolSniffer
	// A Metadata sniffer will be invoked on connection establishment only, with nil body,
	// for both TCP and UDP connections
	// It will not be shown as a traffic type for routing unless there is no other successful sniffing.
	metadataSniffer bool
	network         net.Network
}

type Sniffer struct {
	sniffer []protocolSnifferWithMetadata
}

func NewSniffer(ctx context.Context) *Sniffer {
	ret := &Sniffer{
		sniffer: []protocolSnifferWithMetadata{
			{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false, net.Network_TCP},
			{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false, net.Network_TCP},
			{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false, net.Network_TCP},
			{func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP},
			{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffUTP(b) }, false, net.Network_UDP},
		},
	}
	if sniffer, err := newFakeDNSSniffer(ctx); err == nil {
		others := ret.sniffer
		ret.sniffer = append(ret.sniffer, sniffer)
		fakeDNSThenOthers, err := newFakeDNSThenOthers(ctx, sniffer, others)
		if err == nil {
			ret.sniffer = append([]protocolSnifferWithMetadata{fakeDNSThenOthers}, ret.sniffer...)
		}
	}
	return ret
}

var errUnknownContent = newError("unknown content")

func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) {
	var pendingSniffer []protocolSnifferWithMetadata
	for _, si := range s.sniffer {
		s := si.protocolSniffer
		if si.metadataSniffer || si.network != network {
			continue
		}
		result, err := s(c, payload)
		if err == common.ErrNoClue {
			pendingSniffer = append(pendingSniffer, si)
			continue
		}

		if err == nil && result != nil {
			return result, nil
		}
	}

	if len(pendingSniffer) > 0 {
		s.sniffer = pendingSniffer
		return nil, common.ErrNoClue
	}

	return nil, errUnknownContent
}

func (s *Sniffer) SniffMetadata(c context.Context) (SniffResult, error) {
	var pendingSniffer []protocolSnifferWithMetadata
	for _, si := range s.sniffer {
		s := si.protocolSniffer
		if !si.metadataSniffer {
			pendingSniffer = append(pendingSniffer, si)
			continue
		}
		result, err := s(c, nil)
		if err == common.ErrNoClue {
			pendingSniffer = append(pendingSniffer, si)
			continue
		}

		if err == nil && result != nil {
			return result, nil
		}
	}

	if len(pendingSniffer) > 0 {
		s.sniffer = pendingSniffer
		return nil, common.ErrNoClue
	}

	return nil, errUnknownContent
}

func CompositeResult(domainResult SniffResult, protocolResult SniffResult) SniffResult {
	return &compositeResult{domainResult: domainResult, protocolResult: protocolResult}
}

type compositeResult struct {
	domainResult   SniffResult
	protocolResult SniffResult
}

func (c compositeResult) Protocol() string {
	return c.protocolResult.Protocol()
}

func (c compositeResult) Domain() string {
	return c.domainResult.Domain()
}

func (c compositeResult) ProtocolForDomainResult() string {
	return c.domainResult.Protocol()
}

type SnifferResultComposite interface {
	ProtocolForDomainResult() string
}

type SnifferIsProtoSubsetOf interface {
	IsProtoSubsetOf(protocolName string) bool
}


================================================
FILE: core/xray/app/dispatcher/stats.go
================================================
package dispatcher

import (
	"github.com/xtls/xray-core/common"
	"github.com/xtls/xray-core/common/buf"
	"github.com/xtls/xray-core/features/stats"
)

type SizeStatWriter struct {
	Counter stats.Counter
	Writer  buf.Writer
}

func (w *SizeStatWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
	w.Counter.Add(int64(mb.Len()))
	return w.Writer.WriteMultiBuffer(mb)
}

func (w *SizeStatWriter) Close() error {
	return common.Close(w.Writer)
}

func (w *SizeStatWriter) Interrupt() {
	common.Interrupt(w.Writer)
}


================================================
FILE: core/xray/app/dispatcher/stats_test.go
================================================
package dispatcher_test

import (
	"testing"

	. "github.com/xtls/xray-core/app/dispatcher"
	"github.com/xtls/xray-core/common"
	"github.com/xtls/xray-core/common/buf"
)

type TestCounter int64

func (c *TestCounter) Value() int64 {
	return int64(*c)
}

func (c *TestCounter) Add(v int64) int64 {
	x := int64(*c) + v
	*c = TestCounter(x)
	return x
}

func (c *TestCounter) Set(v int64) int64 {
	*c = TestCounter(v)
	return v
}

func TestStatsWriter(t *testing.T) {
	var c TestCounter
	writer := &SizeStatWriter{
		Counter: &c,
		Writer:  buf.Discard,
	}

	mb := buf.MergeBytes(nil, []byte("abcd"))
	common.Must(writer.WriteMultiBuffer(mb))

	mb = buf.MergeBytes(nil, []byte("efg"))
	common.Must(writer.WriteMultiBuffer(mb))

	if c.Value() != 7 {
		t.Fatal("unexpected counter value. want 7, but got ", c.Value())
	}
}


================================================
FILE: core/xray/distro/all/all.go
================================================
package all

import (
	// The following are necessary as they register handlers in their init functions.

	// Mandatory features. Can't remove unless there are replacements.
	_ "github.com/xtls/xray-core/app/dispatcher"
	_ "github.com/xtls/xray-core/app/proxyman/inbound"
	_ "github.com/xtls/xray-core/app/proxyman/outbound"

	// Default commander and all its services. This is an optional feature.
	//_ "github.com/xtls/xray-core/app/commander"
	//_ "github.com/xtls/xray-core/app/log/command"
	//_ "github.com/xtls/xray-core/app/proxyman/command"
	//_ "github.com/xtls/xray-core/app/stats/command"

	// Developer preview services
	//_ "github.com/xtls/xray-core/app/observatory/command"

	// Other optional features.
	_ "github.com/xtls/xray-core/app/dns"
	_ "github.com/xtls/xray-core/app/dns/fakedns"
	_ "github.com/xtls/xray-core/app/log"
	_ "github.com/xtls/xray-core/app/metrics"
	_ "github.com/xtls/xray-core/app/policy"
	_ "github.com/xtls/xray-core/app/reverse"
	_ "github.com/xtls/xray-core/app/router"
	_ "github.com/xtls/xray-core/app/stats"

	// Fix dependency cycle caused by core import in internet package
	_ "github.com/xtls/xray-core/transport/internet/tagged/taggedimpl"

	// Developer preview features
	//_ "github.com/xtls/xray-core/app/observatory"

	// Inbound and outbound proxies.
	_ "github.com/xtls/xray-core/proxy/blackhole"
	_ "github.com/xtls/xray-core/proxy/dns"
	_ "github.com/xtls/xray-core/proxy/dokodemo"
	_ "github.com/xtls/xray-core/proxy/freedom"
	_ "github.com/xtls/xray-core/proxy/http"
	_ "github.com/xtls/xray-core/proxy/loopback"
	_ "github.com/xtls/xray-core/proxy/shadowsocks"
	_ "github.com/xtls/xray-core/proxy/socks"
	_ "github.com/xtls/xray-core/proxy/trojan"
	_ "github.com/xtls/xray-core/proxy/vless/inbound"
	_ "github.com/xtls/xray-core/proxy/vless/outbound"
	_ "github.com/xtls/xray-core/proxy/vmess/inbound"
	_ "github.com/xtls/xray-core/proxy/vmess/outbound"

	//_ "github.com/xtls/xray-core/proxy/wireguard"

	// Transports
	//_ "github.com/xtls/xray-core/transport/internet/domainsocket"
	_ "github.com/xtls/xray-core/transport/internet/grpc"
	_ "github.com/xtls/xray-core/transport/internet/http"

	//_ "github.com/xtls/xray-core/transport/internet/kcp"
	//_ "github.com/xtls/xray-core/transport/internet/quic"
	_ "github.com/xtls/xray-core/transport/internet/reality"
	_ "github.com/xtls/xray-core/transport/internet/tcp"
	_ "github.com/xtls/xray-core/transport/internet/tls"
	_ "github.com/xtls/xray-core/transport/internet/udp"
	_ "github.com/xtls/xray-core/transport/internet/websocket"

	// Transport headers
	_ "github.com/xtls/xray-core/transport/internet/headers/http"
	_ "github.com/xtls/xray-core/transport/internet/headers/noop"
	_ "github.com/xtls/xray-core/transport/internet/headers/srtp"
	_ "github.com/xtls/xray-core/transport/internet/headers/tls"
	_ "github.com/xtls/xray-core/transport/internet/headers/utp"
	_ "github.com/xtls/xray-core/transport/internet/headers/wechat"
	_ "github.com/xtls/xray-core/transport/internet/headers/wireguard"
)


================================================
FILE: core/xray/inbound.go
================================================
package xray

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"fmt"
	"strconv"

	"github.com/Yuzuki616/V2bX/api/panel"
	"github.com/Yuzuki616/V2bX/conf"
	"github.com/goccy/go-json"
	"github.com/xtls/xray-core/common/net"
	"github.com/xtls/xray-core/core"
	coreConf "github.com/xtls/xray-core/infra/conf"
)

// BuildInbound build Inbound config for different protocol
func buildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag string) (*core.InboundHandlerConfig, error) {
	in := &coreConf.InboundDetourConfig{}
	// Set network protocol
	t := coreConf.TransportProtocol(nodeInfo.Network)
	in.StreamSetting = &coreConf.StreamConfig{Network: &t}
	var err error
	switch nodeInfo.Type {
	case "v2ray":
		err = buildV2ray(config, nodeInfo, in)
	case "trojan":
		err = buildTrojan(config, in)
	case "shadowsocks":
		err = buildShadowsocks(config, nodeInfo, in)
	default:
		return nil, fmt.Errorf("unsupported node type: %s, Only support: V2ray, Trojan, Shadowsocks", nodeInfo.Type)
	}
	if err != nil {
		return nil, err
	}
	// Set server port
	in.PortList = &coreConf.PortList{
		Range: []coreConf.PortRange{{From: uint32(nodeInfo.Port), To: uint32(nodeInfo.Port)}},
	}
	// Set Listen IP address
	ipAddress := net.ParseAddress(config.ListenIP)
	in.ListenOn = &coreConf.Address{Address: ipAddress}
	// Set SniffingConfig
	sniffingConfig := &coreConf.SniffingConfig{
		Enabled:      true,
		DestOverride: &coreConf.StringList{"http", "tls"},
	}
	if config.XrayOptions.DisableSniffing {
		sniffingConfig.Enabled = false
	}
	in.SniffingConfig = sniffingConfig
	if *in.StreamSetting.Network == "tcp" {
		if in.StreamSetting.TCPSettings != nil {
			in.StreamSetting.TCPSettings.AcceptProxyProtocol = config.XrayOptions.EnableProxyProtocol
		} else {
			tcpSetting := &coreConf.TCPConfig{
				AcceptProxyProtocol: config.XrayOptions.EnableProxyProtocol,
			} //Enable proxy protocol
			in.StreamSetting.TCPSettings = tcpSetting
		}
	} else if *in.StreamSetting.Network == "ws" {
		in.StreamSetting.WSSettings = &coreConf.WebSocketConfig{
			AcceptProxyProtocol: config.XrayOptions.EnableProxyProtocol} //Enable proxy protocol
	}
	// Set TLS or Reality settings
	if nodeInfo.Tls {
		if config.CertConfig == nil {
			return nil, errors.New("the CertConfig is not vail")
		}
		switch config.CertConfig.CertMode {
		case "none", "":
			break // disable
		case "reality":
			// Reality
			in.StreamSetting.Security = "reality"
			d, err := json.Marshal(config.CertConfig.RealityConfig.Dest)
			if err != nil {
				return nil, fmt.Errorf("marshal reality dest error: %s", err)
			}
			if len(config.CertConfig.RealityConfig.ShortIds) == 0 {
				config.CertConfig.RealityConfig.ShortIds = []string{""}
			}
			in.StreamSetting.REALITYSettings = &coreConf.REALITYConfig{
				Dest:         d,
				Xver:         config.CertConfig.RealityConfig.Xver,
				ServerNames:  config.CertConfig.RealityConfig.ServerNames,
				PrivateKey:   config.CertConfig.RealityConfig.PrivateKey,
				MinClientVer: config.CertConfig.RealityConfig.MinClientVer,
				MaxClientVer: config.CertConfig.RealityConfig.MaxClientVer,
				MaxTimeDiff:  config.CertConfig.RealityConfig.MaxTimeDiff,
				ShortIds:     config.CertConfig.RealityConfig.ShortIds,
			}
			break
		case "remote":
			if nodeInfo.ExtraConfig.EnableReality == "true" {
				rc := nodeInfo.ExtraConfig.RealityConfig
				in.StreamSetting.Security = "reality"
				d, err := json.Marshal(rc.Dest)
				if err != nil {
					return nil, fmt.Errorf("marshal reality dest error: %s", err)
				}
				if len(rc.ShortIds) == 0 {
					rc.ShortIds = []string{""}
				}
				Xver, _ := strconv.ParseUint(rc.Xver, 10, 64)
				MaxTimeDiff, _ := strconv.ParseUint(rc.Xver, 10, 64)
				in.StreamSetting.REALITYSettings = &coreConf.REALITYConfig{
					Dest:         d,
					Xver:         Xver,
					ServerNames:  rc.ServerNames,
					PrivateKey:   rc.PrivateKey,
					MinClientVer: rc.MinClientVer,
					MaxClientVer: rc.MaxClientVer,
					MaxTimeDiff:  MaxTimeDiff,
					ShortIds:     rc.ShortIds,
				}
				break
			}
		default:
			{
				// Normal tls
				in.StreamSetting.Security = "tls"
				in.StreamSetting.TLSSettings = &coreConf.TLSConfig{
					Certs: []*coreConf.TLSCertConfig{
						{
							CertFile:     config.CertConfig.CertFile,
							KeyFile:      config.CertConfig.KeyFile,
							OcspStapling: 3600,
						},
					},
					RejectUnknownSNI: config.CertConfig.RejectUnknownSni,
				}
			}
		}
	}
	// Support ProxyProtocol for any transport protocol
	if *in.StreamSetting.Network != "tcp" &&
		*in.StreamSetting.Network != "ws" &&
		config.XrayOptions.EnableProxyProtocol {
		socketConfig := &coreConf.SocketConfig{
			AcceptProxyProtocol: config.XrayOptions.EnableProxyProtocol,
			TFO:                 config.XrayOptions.EnableTFO,
		} //Enable proxy protocol
		in.StreamSetting.SocketSettings = socketConfig
	}
	in.Tag = tag
	return in.Build()
}

func buildV2ray(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error {
	if nodeInfo.ExtraConfig.EnableVless == "true" {
		//Set vless
		inbound.Protocol = "vless"
		if config.XrayOptions.EnableFallback {
			// Set fallback
			fallbackConfigs, err := buildVlessFallbacks(config.XrayOptions.FallBackConfigs)
			if err != nil {
				return err
			}
			s, err := json.Marshal(&coreConf.VLessInboundConfig{
				Decryption: "none",
				Fallbacks:  fallbackConfigs,
			})
			if err != nil {
				return fmt.Errorf("marshal vless fallback config error: %s", err)
			}
			inbound.Settings = (*json.RawMessage)(&s)
		} else {
			var err error
			s, err := json.Marshal(&coreConf.VLessInboundConfig{
				Decryption: "none",
			})
			if err != nil {
				return fmt.Errorf("marshal vless config error: %s", err)
			}
			inbound.Settings = (*json.RawMessage)(&s)
		}
	} else {
		// Set vmess
		inbound.Protocol = "vmess"
		var err error
		s, err := json.Marshal(&coreConf.VMessInboundConfig{})
		if err != nil {
			return fmt.Errorf("marshal vmess settings error: %s", err)
		}
		inbound.Settings = (*json.RawMessage)(&s)
	}
	if len(nodeInfo.NetworkSettings) == 0 {
		return nil
	}
	switch nodeInfo.Network {
	case "tcp":
		err := json.Unmarshal(nodeInfo.NetworkSettings, &inbound.StreamSetting.TCPSettings)
		if err != nil {
			return fmt.Errorf("unmarshal tcp settings error: %s", err)
		}
	case "ws":
		err := json.Unmarshal(nodeInfo.NetworkSettings, &inbound.StreamSetting.WSSettings)
		if err != nil {
			return fmt.Errorf("unmarshal ws settings error: %s", err)
		}
	case "grpc":
		err := json.Unmarshal(nodeInfo.NetworkSettings, &inbound.StreamSetting.GRPCConfig)
		if err != nil {
			return fmt.Errorf("unmarshal grpc settings error: %s", err)
		}
	default:
		return errors.New("the network type is not vail")
	}
	return nil
}

func buildTrojan(config *conf.ControllerConfig, inbound *coreConf.InboundDetourConfig) error {
	inbound.Protocol = "trojan"
	if config.XrayOptions.EnableFallback {
		// Set fallback
		fallbackConfigs, err := buildTrojanFallbacks(config.XrayOptions.FallBackConfigs)
		if err != nil {
			return err
		}
		s, err := json.Marshal(&coreConf.TrojanServerConfig{
			Fallbacks: fallbackConfigs,
		})
		inbound.Settings = (*json.RawMessage)(&s)
		if err != nil {
			return fmt.Errorf("marshal trojan fallback config error: %s", err)
		}
	} else {
		s := []byte("{}")
		inbound.Settings = (*json.RawMessage)(&s)
	}
	t := coreConf.TransportProtocol("tcp")
	inbound.StreamSetting = &coreConf.StreamConfig{Network: &t}
	return nil
}

func buildShadowsocks(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error {
	inbound.Protocol = "shadowsocks"
	settings := &coreConf.ShadowsocksServerConfig{
		Cipher: nodeInfo.Cipher,
	}
	p := make([]byte, 32)
	_, err := rand.Read(p)
	if err != nil {
		return fmt.Errorf("generate random password error: %s", err)
	}
	randomPasswd := hex.EncodeToString(p)
	cipher := nodeInfo.Cipher
	if nodeInfo.ServerKey != "" {
		settings.Password = nodeInfo.ServerKey
		randomPasswd = base64.StdEncoding.EncodeToString([]byte(randomPasswd))
		cipher = ""
	}
	defaultSSuser := &coreConf.ShadowsocksUserConfig{
		Cipher:   cipher,
		Password: randomPasswd,
	}
	settings.Users = append(settings.Users, defaultSSuser)
	settings.NetworkList = &coreConf.NetworkList{"tcp", "udp"}
	settings.IVCheck = true
	if config.XrayOptions.DisableIVCheck {
		settings.IVCheck = false
	}
	t := coreConf.TransportProtocol("tcp")
	inbound.StreamSetting = &coreConf.StreamConfig{Network: &t}
	s, err := json.Marshal(settings)
	inbound.Settings = (*json.RawMessage)(&s)
	if err != nil {
		return fmt.Errorf("marshal shadowsocks settings error: %s", err)
	}
	return nil
}

func buildVlessFallbacks(fallbackConfigs []conf.FallBackConfig) ([]*coreConf.VLessInboundFallback, error) {
	if fallbackConfigs == nil {
		return nil, fmt.Errorf("you must provide FallBackConfigs")
	}
	vlessFallBacks := make([]*coreConf.VLessInboundFallback, len(fallbackConfigs))
	for i, c := range fallbackConfigs {
		if c.Dest == "" {
			return nil, fmt.Errorf("dest is required for fallback fialed")
		}
		var dest json.RawMessage
		dest, err := json.Marshal(c.Dest)
		if err != nil {
			return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err)
		}
		vlessFallBacks[i] = &coreConf.VLessInboundFallback{
			Name: c.SNI,
			Alpn: c.Alpn,
			Path: c.Path,
			Dest: dest,
			Xver: c.ProxyProtocolVer,
		}
	}
	return vlessFallBacks, nil
}

func buildTrojanFallbacks(fallbackConfigs []conf.FallBackConfig) ([]*coreConf.TrojanInboundFallback, error) {
	if fallbackConfigs == nil {
		return nil, fmt.Errorf("you must provide FallBackConfigs")
	}

	trojanFallBacks := make([]*coreConf.TrojanInboundFallback, len(fallbackConfigs))
	for i, c := range fallbackConfigs {

		if c.Dest == "" {
			return nil, fmt.Errorf("dest is required for fallback fialed")
		}

		var dest json.RawMessage
		dest, err := json.Marshal(c.Dest)
		if err != nil {
			return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err)
		}
		trojanFallBacks[i] = &coreConf.TrojanInboundFallback{
			Name: c.SNI,
			Alpn: c.Alpn,
			Path: c.Path,
			Dest: dest,
			Xver: c.ProxyProtocolVer,
		}
	}
	return trojanFallBacks, nil
}


================================================
FILE: core/xray/node.go
================================================
package xray

import (
	"context"
	"fmt"
	"github.com/Yuzuki616/V2bX/api/panel"
	"github.com/Yuzuki616/V2bX/conf"
	"github.com/xtls/xray-core/core"
	"github.com/xtls/xray-core/features/inbound"
	"github.com/xtls/xray-core/features/outbound"
)

func (c *Core) AddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error {
	inboundConfig, err := buildInbound(config, info, tag)
	if err != nil {
		return fmt.Errorf("build inbound error: %s", err)
	}
	err = c.addInbound(inboundConfig)
	if err != nil {
		return fmt.Errorf("add inbound error: %s", err)
	}
	outBoundConfig, err := buildOutbound(config, tag)
	if err != nil {
		return fmt.Errorf("build outbound error: %s", err)
	}
	err = c.addOutbound(outBoundConfig)
	if err != nil {
		return fmt.Errorf("add outbound error: %s", err)
	}
	return nil
}

func (c *Core) addInbound(config *core.InboundHandlerConfig) error {
	rawHandler, err := core.CreateObject(c.Server, config)
	if err != nil {
		return err
	}
	handler, ok := rawHandler.(inbound.Handler)
	if !ok {
		return fmt.Errorf("not an InboundHandler: %s", err)
	}
	if err := c.ihm.AddHandler(context.Background(), handler); err != nil {
		return err
	}
	return nil
}

func (c *Core) addOutbound(config *core.OutboundHandlerConfig) error {
	rawHandler, err := core.CreateObject(c.Server, config)
	if err != nil {
		return err
	}
	handler, ok := rawHandler.(outbound.Handler)
	if !ok {
		return fmt.Errorf("not an InboundHandler: %s", err)
	}
	if err := c.ohm.AddHandler(context.Background(), handler); err != nil {
		return err
	}
	return nil
}

func (c *Core) DelNode(tag string) error {
	err := c.removeInbound(tag)
	if err != nil {
		return fmt.Errorf("remove in error: %s", err)
	}
	err = c.removeOutbound(tag)
	if err != nil {
		return fmt.Errorf("remove out error: %s", err)
	}
	return nil
}

func (c *Core) removeInbound(tag string) error {
	return c.ihm.RemoveHandler(context.Background(), tag)
}

func (c *Core) removeOutbound(tag string) error {
	err := c.ohm.RemoveHandler(context.Background(), tag)
	return err
}


================================================
FILE: core/xray/outbound.go
================================================
package xray

import (
	"fmt"
	conf2 "github.com/Yuzuki616/V2bX/conf"
	"github.com/goccy/go-json"
	"github.com/xtls/xray-core/common/net"
	"github.com/xtls/xray-core/core"
	"github.com/xtls/xray-core/infra/conf"
)

// BuildOutbound build freedom outbund config for addoutbound
func buildOutbound(config *conf2.ControllerConfig, tag string) (*core.OutboundHandlerConfig, error) {
	outboundDetourConfig := &conf.OutboundDetourConfig{}
	outboundDetourConfig.Protocol = "freedom"
	outboundDetourConfig.Tag = tag

	// Build Send IP address
	if config.SendIP != "" {
		ipAddress := net.ParseAddress(config.SendIP)
		outboundDetourConfig.SendThrough = &conf.Address{Address: ipAddress}
	}

	// Freedom Protocol setting
	var domainStrategy = "Asis"
	if config.XrayOptions.EnableDNS {
		if config.XrayOptions.DNSType != "" {
			domainStrategy = config.XrayOptions.DNSType
		} else {
			domainStrategy = "UseIP"
		}
	}
	proxySetting := &conf.FreedomConfig{
		DomainStrategy: domainStrategy,
	}
	var setting json.RawMessage
	setting, err := json.Marshal(proxySetting)
	if err != nil {
		return nil, fmt.Errorf("marshal proxy config error: %s", err)
	}
	outboundDetourConfig.Settings = &setting
	return outboundDetourConfig.Build()
}


================================================
FILE: core/xray/ss.go
================================================
package xray

import (
	"encoding/base64"
	"github.com/Yuzuki616/V2bX/api/panel"
	"github.com/Yuzuki616/V2bX/common/format"
	"github.com/xtls/xray-core/common/protocol"
	"github.com/xtls/xray-core/common/serial"
	"github.com/xtls/xray-core/proxy/shadowsocks"
	"github.com/xtls/xray-core/proxy/shadowsocks_2022"
	"strings"
)

func buildSSUsers(tag string, userInfo []panel.UserInfo, cypher string, serverKey string) (users []*protocol.User) {
	users = make([]*protocol.User, len(userInfo))
	for i := range userInfo {
		users[i] = buildSSUser(tag, &userInfo[i], cypher, serverKey)
	}
	return users
}

func buildSSUser(tag string, userInfo *panel.UserInfo, cypher string, serverKey string) (user *protocol.User) {
	if serverKey == "" {
		ssAccount := &shadowsocks.Account{
			Password:   userInfo.Uuid,
			CipherType: getCipherFromString(cypher),
		}
		return &protocol.User{
			Level:   0,
			Email:   format.UserTag(tag, userInfo.Uuid),
			Account: serial.ToTypedMessage(ssAccount),
		}
	} else {
		var keyLength int
		switch cypher {
		case "2022-blake3-aes-128-gcm":
			keyLength = 16
		case "2022-blake3-aes-256-gcm":
			keyLength = 32
		}
		ssAccount := &shadowsocks_2022.User{
			Key: base64.StdEncoding.EncodeToString([]byte(userInfo.Uuid[:keyLength])),
		}
		return &protocol.User{
			Level:   0,
			Email:   format.UserTag(tag, userInfo.Uuid),
			Account: serial.ToTypedMessage(ssAccount),
		}
	}
}

func getCipherFromString(c string) shadowsocks.CipherType {
	switch strings.ToLower(c) {
	case "aes-128-gcm", "aead_aes_128_gcm":
		return shadowsocks.CipherType_AES_128_GCM
	case "aes-256-gcm", "aead_aes_256_gcm":
		return shadowsocks.CipherType_AES_256_GCM
	case "chacha20-poly1305", "aead_chacha20_poly1305", "chacha20-ietf-poly1305":
		return shadowsocks.CipherType_CHACHA20_POLY1305
	case "none", "plain":
		return shadowsocks.CipherType_NONE
	default:
		return shadowsocks.CipherType_UNKNOWN
	}
}


================================================
FILE: core/xray/trojan.go
================================================
package xray

import (
	"github.com/Yuzuki616/V2bX/api/panel"
	"github.com/Yuzuki616/V2bX/common/format"
	"github.com/xtls/xray-core/common/protocol"
	"github.com/xtls/xray-core/common/serial"
	"github.com/xtls/xray-core/proxy/trojan"
)

func buildTrojanUsers(tag string, userInfo []panel.UserInfo) (users []*protocol.User) {
	users = make([]*protocol.User, len(userInfo))
	for i := range userInfo {
		users[i] = buildTrojanUser(tag, &(userInfo)[i])
	}
	return users
}

func buildTrojanUser(tag string, userInfo *panel.UserInfo) (user *protocol.User) {
	trojanAccount := &trojan.Account{
		Password: userInfo.Uuid,
	}
	return &protocol.User{
		Level:   0,
		Email:   format.UserTag(tag, userInfo.Uuid),
		Account: serial.ToTypedMessage(trojanAccount),
	}
}


================================================
FILE: core/xray/user.go
================================================
package xray

import (
	"context"
	"fmt"

	"github.com/Yuzuki616/V2bX/api/panel"
	"github.com/Yuzuki616/V2bX/common/format"
	vCore "github.com/Yuzuki616/V2bX/core"
	"github.com/xtls/xray-core/common/protocol"
	"github.com/xtls/xray-core/proxy"
)

func (c *Core) GetUserManager(tag string) (proxy.UserManager, error) {
	handler, err := c.ihm.GetHandler(context.Background(), tag)
	if err != nil {
		return nil, fmt.Errorf("no such inbound tag: %s", err)
	}
	inboundInstance, ok := handler.(proxy.GetInbound)
	if !ok {
		return nil, fmt.Errorf("handler %s is not implement proxy.GetInbound", tag)
	}
	userManager, ok := inboundInstance.GetInbound().(proxy.UserManager)
	if !ok {
		return nil, fmt.Errorf("handler %s is not implement proxy.UserManager", tag)
	}
	return userManager, nil
}

func (c *Core) DelUsers(users []panel.UserInfo, tag string) error {
	userManager, err := c.GetUserManager(tag)
	if err != nil {
		return fmt.Errorf("get user manager error: %s", err)
	}
	var up, down, user string
	for i := range users {
		user = format.UserTag(tag, users[i].Uuid)
		err = userManager.RemoveUser(context.Background(), user)
		if err != nil {
			return err
		}
		up = "user>>>" + user + ">>>traffic>>>uplink"
		down = "user>>>" + user + ">>>traffic>>>downlink"
		c.shm.UnregisterCounter(up)
		c.shm.UnregisterCounter(down)
	}
	return nil
}

func (c *Core) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) {
	upName := "user>>>" + format.UserTag(tag, uuid) + ">>>traffic>>>uplink"
	downName := "user>>>" + format.UserTag(tag, uuid) + ">>>traffic>>>downlink"
	upCounter := c.shm.GetCounter(upName)
	downCounter := c.shm.GetCounter(downName)
	if reset {
		if upCounter != nil {
			up = upCounter.Set(0)
		}
		if downCounter != nil {
			down = downCounter.Set(0)
		}
	} else {
		if upCounter != nil {
			up = upCounter.Value()
		}
		if downCounter != nil {
			down = downCounter.Value()
		}
	}
	return up, down
}

func (c *Core) AddUsers(p *vCore.AddUsersParams) (added int, err error) {
	users := make([]*protocol.User, 0, len(p.UserInfo))
	switch p.NodeInfo.Type {
	case "v2ray":
		if p.NodeInfo.ExtraConfig.EnableVless == "true" {
			users = buildVlessUsers(p.Tag, p.UserInfo, p.NodeInfo.ExtraConfig.VlessFlow)
		} else {
			users = buildVmessUsers(p.Tag, p.UserInfo)
		}
	case "trojan":
		users = buildTrojanUsers(p.Tag, p.UserInfo)
	case "shadowsocks":
		users = buildSSUsers(p.Tag,
			p.UserInfo,
			p.NodeInfo.Cipher,
			p.NodeInfo.ServerKey)
	default:
		return 0, fmt.Errorf("unsupported node type: %s", p.NodeInfo.Type)
	}
	man, err := c.GetUserManager(p.Tag)
	if err != nil {
		return 0, fmt.Errorf("get user manager error: %s", err)
	}
	for _, u := range users {
		mUser, err := u.ToMemoryUser()
		if err != nil {
			return 0, err
		}
		err = man.AddUser(context.Background(), mUser)
		if err != nil {
			return 0, err
		}
	}
	return len(users), nil
}


================================================
FILE: core/xray/vmess.go
================================================
package xray

import (
	"github.com/Yuzuki616/V2bX/api/panel"
	"github.com/Yuzuki616/V2bX/common/format"
	"github.com/xtls/xray-core/common/protocol"
	"github.com/xtls/xray-core/common/serial"
	"github.com/xtls/xray-core/infra/conf"
	"github.com/xtls/xray-core/proxy/vless"
)

func buildVmessUsers(tag string, userInfo []panel.UserInfo) (users []*protocol.User) {
	users = make([]*protocol.User, len(userInfo))
	for i, user := range userInfo {
		users[i] = buildVmessUser(tag, &user)
	}
	return users
}

func buildVmessUser(tag string, userInfo *panel.UserInfo) (user *protocol.User) {
	vmessAccount := &conf.VMessAccount{
		ID:       userInfo.Uuid,
		Security: "auto",
	}
	return &protocol.User{
		Level:   0,
		Email:   format.UserTag(tag, userInfo.Uuid), // Uid: InboundTag|email
		Account: serial.ToTypedMessage(vmessAccount.Build()),
	}
}

func buildVlessUsers(tag string, userInfo []panel.UserInfo, flow string) (users []*protocol.User) {
	users = make([]*protocol.User, len(userInfo))
	for i := range userInfo {
		users[i] = buildVlessUser(tag, &(userInfo)[i], flow)
	}
	return users
}

func buildVlessUser(tag string, userInfo *panel.UserInfo, flow string) (user *protocol.User) {
	vlessAccount := &vless.Account{
		Id: userInfo.Uuid,
	}
	vlessAccount.Flow = flow
	return &protocol.User{
		Level:   0,
		Email:   format.UserTag(tag, userInfo.Uuid),
		Account: serial.ToTypedMessage(vlessAccount),
	}
}


================================================
FILE: core/xray/xray.go
================================================
package xray

import (
	"os"
	"sync"

	"github.com/Yuzuki616/V2bX/conf"
	vCore "github.com/Yuzuki616/V2bX/core"
	"github.com/Yuzuki616/V2bX/core/xray/app/dispatcher"
	_ "github.com/Yuzuki616/V2bX/core/xray/distro/all"
	"github.com/goccy/go-json"
	log "github.com/sirupsen/logrus"
	"github.com/xtls/xray-core/app/proxyman"
	"github.com/xtls/xray-core/app/stats"
	"github.com/xtls/xray-core/common/serial"
	"github.com/xtls/xray-core/core"
	"github.com/xtls/xray-core/features/inbound"
	"github.com/xtls/xray-core/features/outbound"
	"github.com/xtls/xray-core/features/routing"
	statsFeature "github.com/xtls/xray-core/features/stats"
	coreConf "github.com/xtls/xray-core/infra/conf"
)

func init() {
	vCore.RegisterCore("xray", New)
}

// Core Structure
type Core struct {
	access     sync.Mutex
	Server     *core.Instance
	ihm        inbound.Manager
	ohm        outbound.Manager
	shm        statsFeature.Manager
	dispatcher *dispatcher.DefaultDispatcher
}

func New(c *conf.CoreConfig) (vCore.Core, error) {
	return &Core{Server: getCore(c.XrayConfig)}, nil
}

func parseConnectionConfig(c *conf.ConnectionConfig) (policy *coreConf.Policy) {
	policy = &coreConf.Policy{
		StatsUserUplink:   true,
		StatsUserDownlink: true,
		Handshake:         &c.Handshake,
		ConnectionIdle:    &c.ConnIdle,
		UplinkOnly:        &c.UplinkOnly,
		DownlinkOnly:      &c.DownlinkOnly,
		BufferSize:        &c.BufferSize,
	}
	return
}

func getCore(c *conf.XrayConfig) *core.Instance {
	os.Setenv("XRAY_LOCATION_ASSET", c.AssetPath)
	// Log Config
	coreLogConfig := &coreConf.LogConfig{}
	coreLogConfig.LogLevel = c.LogConfig.Level
	coreLogConfig.AccessLog = c.LogConfig.AccessPath
	coreLogConfig.ErrorLog = c.LogConfig.ErrorPath
	// DNS config
	coreDnsConfig := &coreConf.DNSConfig{}
	os.Setenv("XRAY_DNS_PATH", "")
	if c.DnsConfigPath != "" {
		if f, err := os.Open(c.DnsConfigPath); err != nil {
			log.WithField("err", err).Panic("Failed to read DNS config file")
		} else {
			if err = json.NewDecoder(f).Decode(coreDnsConfig); err != nil {
				log.WithField("err", err).Panic("Failed to unmarshal DNS config")
			}
		}
		os.Setenv("XRAY_DNS_PATH", c.DnsConfigPath)
	}
	dnsConfig, err := coreDnsConfig.Build()
	if err != nil {
		log.WithField("err", err).Panic("Failed to understand DNS config, Please check: https://xtls.github.io/config/dns.html for help")
	}
	// Routing config
	coreRouterConfig := &coreConf.RouterConfig{}
	if c.RouteConfigPath != "" {
		if f, err := os.Open(c.RouteConfigPath); err != nil {
			log.WithField("err", err).Panic("Failed to read Routing config file")
		} else {
			if err = json.NewDecoder(f).Decode(coreRouterConfig); err != nil {
				log.WithField("err", err).Panic("Failed to unmarshal Routing config")
			}
		}
	}
	routeConfig, err := coreRouterConfig.Build()
	if err != nil {
		log.WithField("err", err).Panic("Failed to understand Routing config  Please check: https://xtls.github.io/config/routing.html")
	}
	// Custom Inbound config
	var coreCustomInboundConfig []coreConf.InboundDetourConfig
	if c.InboundConfigPath != "" {
		if f, err := os.Open(c.InboundConfigPath); err != nil {
			log.WithField("err", err).Panic("Failed to read Custom Inbound config file")
		} else {
			if err = json.NewDecoder(f).Decode(&coreCustomInboundConfig); err != nil {
				log.WithField("err", err).Panic("Failed to unmarshal Custom Inbound config")
			}
		}
	}
	var inBoundConfig []*core.InboundHandlerConfig
	for _, config := range coreCustomInboundConfig {
		oc, err := config.Build()
		if err != nil {
			log.WithField("err", err).Panic("Failed to understand Inbound config, Please check: https://xtls.github.io/config/inbound.html for help")
		}
		inBoundConfig = append(inBoundConfig, oc)
	}
	// Custom Outbound config
	var coreCustomOutboundConfig []coreConf.OutboundDetourConfig
	if c.OutboundConfigPath != "" {
		if f, err := os.Open(c.OutboundConfigPath); err != nil {
			log.WithField("err", err).Panic("Failed to read Custom Outbound config file")
		} else {
			if err = json.NewDecoder(f).Decode(&coreCustomOutboundConfig); err != nil {
				log.WithField("err", err).Panic("Failed to unmarshal Custom Outbound config")
			}
		}
	}
	var outBoundConfig []*core.OutboundHandlerConfig
	for _, config := range coreCustomOutboundConfig {
		oc, err := config.Build()
		if err != nil {
			log.WithField("err", err).Panic("Failed to understand Outbound config, Please check: https://xtls.github.io/config/outbound.html for help")
		}
		outBoundConfig = append(outBoundConfig, oc)
	}
	// Policy config
	levelPolicyConfig := parseConnectionConfig(c.ConnectionConfig)
	corePolicyConfig := &coreConf.PolicyConfig{}
	corePolicyConfig.Levels = map[uint32]*coreConf.Policy{0: levelPolicyConfig}
	policyConfig, _ := corePolicyConfig.Build()
	// Build Core conf
	config := &core.Config{
		App: []*serial.TypedMessage{
			serial.ToTypedMessage(coreLogConfig.Build()),
			serial.ToTypedMessage(&dispatcher.Config{}),
			serial.ToTypedMessage(&stats.Config{}),
			serial.ToTypedMessage(&proxyman.InboundConfig{}),
			serial.ToTypedMessage(&proxyman.OutboundConfig{}),
			serial.ToTypedMessage(policyConfig),
			serial.ToTypedMessage(dnsConfig),
			serial.ToTypedMessage(routeConfig),
		},
		Inbound:  inBoundConfig,
		Outbound: outBoundConfig,
	}
	server, err := core.New(config)
	if err != nil {
		log.WithField("err", err).Panic("failed to create instance")
	}
	log.Info("Xray Core Version: ", core.Version())
	return server
}

// Start the Core
func (c *Core) Start() error {
	c.access.Lock()
	defer c.access.Unlock()
	if err := c.Server.Start(); err != nil {
		return err
	}
	c.shm = c.Server.GetFeature(statsFeature.ManagerType()).(statsFeature.Manager)
	c.ihm = c.Server.GetFeature(inbound.ManagerType()).(inbound.Manager)
	c.ohm = c.Server.GetFeature(outbound.ManagerType()).(outbound.Manager)
	c.dispatcher = c.Server.GetFeature(routing.DispatcherType()).(*dispatcher.DefaultDispatcher)
	return nil
}

// Close  the core
func (c *Core) Close() error {
	c.access.Lock()
	defer c.access.Unlock()
	c.ihm = nil
	c.ohm = nil
	c.shm = nil
	c.dispatcher = nil
	err := c.Server.Close()
	if err != nil {
		return err
	}
	return nil
}

func (c *Core) Protocols() []string {
	return []string{
		"v2ray",
		"shadowsocks",
		"trojan",
	}
}


================================================
FILE: example/config.yml.example
================================================
CoreConfig:
  Type: "xray" # Core type, default support "xray" and "hy". If you need many cores, use " " to split
  XrayConfig:
    Log:
      Level: warning # Log level: none, error, warning, info, debug
      AccessPath: # /etc/XrayR/access.Log
      ErrorPath: # /etc/XrayR/error.log
    DnsConfigPath: # /etc/XrayR/dns.json # Path to dns config, check https://xtls.github.io/config/dns.html for help
    RouteConfigPath: # /etc/XrayR/route.json # Path to route config, check https://xtls.github.io/config/routing.html for help
    InboundConfigPath: # /etc/XrayR/custom_inbound.json # Path to custom inbound config, check https://xtls.github.io/config/inbound.html for help
    OutboundConfigPath: # /etc/XrayR/custom_outbound.json # Path to custom outbound config, check https://xtls.github.io/config/outbound.html for help
    ConnectionConfig:
      Handshake: 4 # Handshake time limit, Second
      ConnIdle: 30 # Connection idle time limit, Second
      UplinkOnly: 2 # Time limit when the connection downstream is closed, Second
      DownlinkOnly: 4 # Time limit when the connection is closed after the uplink is closed, Second
      BufferSize: 64 # The internal cache size of each connection, kB
Nodes:
  - ApiConfig:
      ApiHost: "http://127.0.0.1:667"
      ApiKey: "123"
      NodeID: 41
      NodeType: V2ray # Node type: V2ray, Shadowsocks, Trojan
      Timeout: 30 # Timeout for the api request
      RuleListPath: # /etc/XrayR/rulelist Path to local rulelist file
    ControllerConfig:
      ListenIP: 0.0.0.0 # IP address you want to listen
      SendIP: 0.0.0.0 # IP address you want to send pacakage
      XrayOptions:
        EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
        DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy
        EnableTFO: false # Enable TCP Fast Open
        EnableProxyProtocol: false # Only works for WebSocket and TCP
        EnableFallback: false # Only support for Trojan and Vless
        FallBackConfigs: # Support multiple fallbacks
          - SNI: # TLS SNI(Server Name Indication), Empty for any
            Alpn: # Alpn, Empty for any
            Path: # HTTP PATH, Empty for any
            Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details.
            ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable
      HyOptions:
        Resolver: "udp://1.1.1.1:53" # DNS resolver address
        ResolvePreference: 64 # DNS IPv4/IPv6 preference. Available options: "64" (IPv6 first, fallback to IPv4), "46" (IPv4 first, fallback to IPv6), "6" (IPv6 only), "4" (IPv4 only)
        SendDevice: "eth0" # Bind device for outbound connections (usually requires root)
      LimitConfig:
        EnableRealtime: false # Check device limit on real time
        SpeedLimit: 0 # Mbps, Local settings will replace remote settings, 0 means disable
        DeviceLimit: 0 # Local settings will replace remote settings, 0 means disable
        ConnLimit: 0 # Connecting limit, only working for TCP, 0mean
        EnableIpRecorder: false # Enable online ip report
        IpRecorderConfig:
          Type: "Recorder" # Recorder type: Recorder, Redis
          RecorderConfig:
            Url: "http://127.0.0.1:123" # Report url
            Token: "123" # Report token
            Timeout: 10 # Report timeout, sec.
          RedisConfig:
            Address: "127.0.0.1:6379" # Redis address
            Password: "" # Redis password
            DB: 0 # Redis DB
            Expiry: 60 # redis expiry time, sec.
          Periodic: 60 # Report interval, sec.
          EnableIpSync: false # Enable online ip sync
        EnableDynamicSpeedLimit: false # Enable dynamic speed limit
        DynamicSpeedLimitConfig:
          Periodic: 60 # Time to check the user traffic , sec.
          Traffic: 0 # Traffic limit, MB
          SpeedLimit: 0 # Speed limit, Mbps
          ExpireTime: 0 # Time limit, sec.
      CertConfig:
        CertMode: dns # Option about how to get certificate: none, file, http, dns, reality, remote. Choose "none" will forcedly disable the tls config.
        CertDomain: "node1.test.com" # Domain to cert
        CertFile: /etc/XrayR/cert/node1.test.com.cert # Provided if the CertMode is file
        KeyFile: /etc/XrayR/cert/node1.test.com.key
        Provider: alidns # DNS cert provider, Get the full support list here: https://go-acme.github.io/lego/dns/
        Email: test@me.com
        DNSEnv: # DNS ENV option used by DNS provider
          ALICLOUD_ACCESS_KEY: aaa
          ALICLOUD_SECRET_KEY: bbb
        RealityConfig: # This config like RealityObject for xray-core, please check https://xtls.github.io/config/transport.html#realityobject
          Dest: 80 # Same fallback dest
          Xver: 0 # Same fallback xver
          ServerNames:
            - "example.com"
            - "www.example.com"
          PrivateKey: "" # Private key for server
          MinClientVer: "" # Min client version
          MaxClientVer: "" # Max client version
          MaxTimeDiff: 0 # Max time difference, ms
          ShortIds: # Short ids
            - ""
            - "0123456789abcdef"
  # -
  #   ApiConfig:
  #     ApiHost: "http://127.0.0.1:668"
  #     ApiKey: "123"
  #     NodeID: 4
  #     NodeType: Shadowsocks # Node type: V2ray, Shadowsocks, Trojan
  #     Timeout: 30 # Timeout for the api request
  #     EnableVless: false # Enable Vless for V2ray Type
  #     EnableXTLS: false # Enable XTLS for V2ray and Trojan
  #     SpeedLimit: 0 # Mbps, Local settings will replace remote settings
  #     DeviceLimit: 0 # Local settings will replace remote settings
  #   ControllerConfig:
  #     ListenIP: 0.0.0.0 # IP address you want to listen
  #     EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
  #     CertConfig:
  #       CertMode: dns # Option about how to get certificate: none, file, http, dns
  #       CertDomain: "node1.test.com" # Domain to cert
  #       CertFile: /etc/XrayR/cert/node1.test.com.cert # Provided if the CertMode is file
  #       KeyFile: /etc/XrayR/cert/node1.test.com.pem
  #       Provider: alidns # DNS cert provider, Get the full support list here: https://go-acme.github.io/lego/dns/
  #       Email: test@me.com
  #       DNSEnv: # DNS ENV option used by DNS provider
  #         ALICLOUD_ACCESS_KEY: aaa
  #         ALICLOUD_SECRET_KEY: bbb



================================================
FILE: example/custom_inbound.json
================================================
[
    {
        "listen": "0.0.0.0",
        "port": 1234,
        "protocol": "socks",
        "settings": {
            "auth": "noauth",
            "accounts": [
                {
                    "user": "my-username",
                    "pass": "my-password"
                }
            ],
            "udp": false,
            "ip": "127.0.0.1",
            "userLevel": 0
        }
    }
]

================================================
FILE: example/custom_outbound.json
================================================
[
    {
        "tag": "IPv4_out",
        "protocol": "freedom",
        "settings": {}
    },
    {
        "tag": "IPv6_out",
        "protocol": "freedom",
        "settings": {
            "domainStrategy": "UseIPv6"
        }
    },
    {
        "tag": "socks5-warp",
        "protocol": "socks",
        "settings": {
            "servers": [{
                "address": "127.0.0.1",
                "port": 40000
            }]
        }
    },
    {
        "protocol": "blackhole",
        "tag": "block"
    }
]

================================================
FILE: example/dns.json
================================================
{
    "servers": [
        "1.1.1.1",
        "8.8.8.8",
        "localhost"
    ],
    "tag": "dns_inbound"
}

================================================
FILE: example/route.json
================================================
{
    "domainStrategy": "IPOnDemand",
    "rules": [
        {
            "type": "field",
            "outboundTag": "block",
            "ip": [
                "geoip:private"
            ]
        },
        {
            "type": "field",
            "outboundTag": "block",
            "protocol": [
                "bittorrent"
            ]
        },
        {
            "type": "field",
            "outboundTag": "socks5-warp",
            "domain": [""]
        },
        {
            "type": "field",
            "outboundTag": "IPv6_out",
            "domain": [
                "geosite:netflix"
            ]
        },
        {
            "type": "field",
            "outboundTag": "IPv4_out",
            "network": "udp,tcp"
        }
    ]
}

================================================
FILE: example/rulelist
================================================
(.+\.|^)(360|so)\.(cn|com)
baidu.com
google.com

================================================
FILE: go.mod
================================================
module github.com/Yuzuki616/V2bX

go 1.19

require (
	github.com/Yuzuki616/hysteria/core v0.0.0-20230722103310-05508b7e5490
	github.com/Yuzuki616/quic-go v0.34.1
	github.com/beevik/ntp v1.2.0
	github.com/folbricht/routedns v0.1.20
	github.com/fsnotify/fsnotify v1.6.0
	github.com/go-acme/lego/v4 v4.13.3
	github.com/go-redis/redis/v8 v8.11.5
	github.com/go-resty/resty/v2 v2.7.0
	github.com/goccy/go-json v0.10.2
	github.com/hashicorp/go-multierror v1.1.1
	github.com/juju/ratelimit v1.0.2
	github.com/oschwald/geoip2-golang v1.9.0
	github.com/sirupsen/logrus v1.9.3
	github.com/spf13/cobra v1.7.0
	github.com/xtls/xray-core v1.8.3
	golang.org/x/crypto v0.11.0
	golang.org/x/sys v0.10.0
	google.golang.org/protobuf v1.31.0
	gopkg.in/yaml.v3 v3.0.1
)

require (
	cloud.google.com/go/compute v1.19.1 // indirect
	cloud.google.com/go/compute/metadata v0.2.3 // indirect
	github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
	github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect
	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect
	github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0 // indirect
	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0 // indirect
	github.com/Azure/go-autorest v14.2.0+incompatible // indirect
	github.com/Azure/go-autorest/autorest v0.11.24 // indirect
	github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
	github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect
	github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
	github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
	github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
	github.com/Azure/go-autorest/logger v0.2.1 // indirect
	github.com/Azure/go-autorest/tracing v0.6.0 // indirect
	github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
	github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
	github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91 // indirect
	github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
	github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect
	github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
	github.com/andybalholm/brotli v1.0.5 // indirect
	github.com/aws/aws-sdk-go v1.39.0 // indirect
	github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
	github.com/cenkalti/backoff/v4 v4.2.1 // indirect
	github.com/cespare/xxhash/v2 v2.2.0 // indirect
	github.com/civo/civogo v0.3.11 // indirect
	github.com/cloudflare/cloudflare-go v0.70.0 // indirect
	github.com/coreos/go-iptables v0.6.0 // indirect
	github.com/cpu/goacmedns v0.1.1 // indirect
	github.com/davecgh/go-spew v1.1.1 // indirect
	github.com/deepmap/oapi-codegen v1.9.1 // indirect
	github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
	github.com/dimchansky/utfbom v1.1.1 // indirect
	github.com/dnsimple/dnsimple-go v1.2.0 // indirect
	github.com/exoscale/egoscale v0.100.1 // indirect
	github.com/fatih/structs v1.1.0 // indirect
	github.com/francoispqt/gojay v1.2.13 // indirect
	github.com/gaukas/godicttls v0.0.3 // indirect
	github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // indirect
	github.com/go-errors/errors v1.0.1 // indirect
	github.com/go-jose/go-jose/v3 v3.0.0 // indirect
	github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
	github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
	github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
	github.com/golang/mock v1.6.0 // indirect
	github.com/golang/protobuf v1.5.3 // indirect
	github.com/google/btree v1.1.2 // indirect
	github.com/google/go-querystring v1.1.0 // indirect
	github.com/google/gopacket v1.1.19 // indirect
	github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect
	github.com/google/uuid v1.3.0 // indirect
	github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
	github.com/googleapis/gax-go/v2 v2.7.1 // indirect
	github.com/gophercloud/gophercloud v1.0.0 // indirect
	github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
	github.com/gorilla/websocket v1.5.0 // indirect
	github.com/hashicorp/errwrap v1.0.0 // indirect
	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
	github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
	github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect
	github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
	github.com/inconshreveable/mousetrap v1.1.0 // indirect
	github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
	github.com/jmespath/go-jmespath v0.4.0 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/jtacoma/uritemplates v1.0.0 // indirect
	github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
	github.com/klauspost/compress v1.16.6 // indirect
	github.com/klauspost/cpuid/v2 v2.2.5 // indirect
	github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
	github.com/kylelemons/godebug v1.1.0 // indirect
	github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
	github.com/labbsr0x/goh v1.0.1 // indirect
	github.com/linode/linodego v1.17.2 // indirect
	github.com/liquidweb/go-lwApi v0.0.5 // indirect
	github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
	github.com/liquidweb/liquidweb-go v1.6.3 // indirect
	github.com/lucas-clemente/quic-go v0.31.1 // indirect
	github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect
	github.com/marten-seemann/qpack v0.3.0 // indirect
	github.com/marten-seemann/qtls-go1-18 v0.1.4 // indirect
	github.com/marten-seemann/qtls-go1-19 v0.1.2 // indirect
	github.com/mattn/go-isatty v0.0.19 // indirect
	github.com/miekg/dns v1.1.55 // indirect
	github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
	github.com/mitchellh/go-homedir v1.1.0 // indirect
	github.com/mitchellh/mapstructure v1.5.0 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
	github.com/nrdcg/auroradns v1.1.0 // indirect
	github.com/nrdcg/desec v0.7.0 // indirect
	github.com/nrdcg/dnspod-go v0.4.0 // indirect
	github.com/nrdcg/freemyip v0.2.0 // indirect
	github.com/nrdcg/goinwx v0.8.2 // indirect
	github.com/nrdcg/namesilo v0.2.1 // indirect
	github.com/nrdcg/nodion v0.1.0 // indirect
	github.com/nrdcg/porkbun v0.2.0 // indirect
	github.com/nzdjb/go-metaname v1.0.0 // indirect
	github.com/onsi/ginkgo/v2 v2.11.0 // indirect
	github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
	github.com/oschwald/maxminddb-golang v1.11.0 // indirect
	github.com/ovh/go-ovh v1.4.1 // indirect
	github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
	github.com/pion/dtls/v2 v2.2.4 // indirect
	github.com/pion/logging v0.2.2 // indirect
	github.com/pion/transport/v2 v2.0.0 // indirect
	github.com/pion/udp v0.1.4 // indirect
	github.com/pires/go-proxyproto v0.7.0 // indirect
	github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
	github.com/pkg/errors v0.9.1 // indirect
	github.com/pmezard/go-difflib v1.0.0 // indirect
	github.com/pquerna/otp v1.4.0 // indirect
	github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
	github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
	github.com/quic-go/quic-go v0.35.1 // indirect
	github.com/refraction-networking/utls v1.3.2 // indirect
	github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
	github.com/sacloud/api-client-go v0.2.8 // indirect
	github.com/sacloud/go-http v0.1.6 // indirect
	github.com/sacloud/iaas-api-go v1.11.1 // indirect
	github.com/sacloud/packages-go v0.0.9 // indirect
	github.com/sagernet/sing v0.2.5 // indirect
	github.com/sagernet/sing-shadowsocks v0.2.2 // indirect
	github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
	github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 // indirect
	github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
	github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 // indirect
	github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
	github.com/softlayer/softlayer-go v1.1.2 // indirect
	github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
	github.com/spf13/cast v1.3.1 // indirect
	github.com/spf13/pflag v1.0.5 // indirect
	github.com/stretchr/objx v0.5.0 // indirect
	github.com/stretchr/testify v1.8.4 // indirect
	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect
	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect
	github.com/transip/gotransip/v6 v6.20.0 // indirect
	github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect
	github.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a // indirect
	github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe // indirect
	github.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c // indirect
	github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
	github.com/vinyldns/go-vinyldns v0.9.16 // indirect
	github.com/vultr/govultr/v2 v2.17.2 // indirect
	github.com/xtls/reality v0.0.0-20230613075828-e07c3b04b983 // indirect
	github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f // indirect
	github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect
	go.opencensus.io v0.24.0 // indirect
	go.uber.org/atomic v1.11.0 // indirect
	go.uber.org/ratelimit v0.2.0 // indirect
	golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
	golang.org/x/mod v0.11.0 // indirect
	golang.org/x/net v0.11.0 // indirect
	golang.org/x/oauth2 v0.9.0 // indirect
	golang.org/x/text v0.11.0 // indirect
	golang.org/x/time v0.3.0 // indirect
	golang.org/x/tools v0.10.0 // indirect
	google.golang.org/api v0.114.0 // indirect
	google.golang.org/appengine v1.6.7 // indirect
	google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
	google.golang.org/grpc v1.56.0 // indirect
	gopkg.in/ini.v1 v1.67.0 // indirect
	gopkg.in/ns1/ns1-go.v2 v2.7.6 // indirect
	gopkg.in/yaml.v2 v2.4.0 // indirect
	gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect
	lukechampine.com/blake3 v1.2.1 // indirect
)

exclude gvisor.dev/gvisor v0.0.0-20230313184804-9bf6dd27710d


================================================
FILE: go.sum
================================================
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY=
cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 h1:Dy3M9aegiI7d7PF1LUdjbVigJReo+QOceYsMyFh9qoE=
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesnDfrtF6RFUGzQfLo=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 h1:8kDqDngH+DmVBiCtIjCFTGa7MBnsIOkF9IccInFEbjk=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0 h1:8iR6OLffWWorFdzL2JFCab5xpD8VKEE2DUBBl+HNTDY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0/go.mod h1:copqlcjMWc/wgQ1N2fzsJFQxDdqKGg1EQt8T5wJMOGE=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0 h1:rR8ZW79lE/ppfXTfiYSnMFv5EzmVuY4pfZWIkscIJ64=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0/go.mod h1:y2zXtLSMM/X5Mfawq0lOftpWn3f4V6OCsRdINsvWBPI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.24 h1:1fIGgHKqVm54KIPT+q8Zmd1QlVsmHqeUGso5qm2BqqE=
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 h1:wkAZRgT/pn8HhFyzfe9UnqOjJYqlembgCTi72Bm/xKk=
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY=
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24=
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91 h1:vX+gnvBc56EbWYrmlhYbFYRaeikAke1GL84N4BEYOFE=
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91/go.mod h1:cDLGBht23g0XQdLjzn6xOGXDkLK182YfINAaZEQLCHQ=
github.com/Yuzuki616/hysteria/core v0.0.0-20230722103310-05508b7e5490 h1:OqS0ywNFR0fEWteLyTcexUNkpdb3vTl5EdMn9gMJMCc=
github.com/Yuzuki616/hysteria/core v0.0.0-20230722103310-05508b7e5490/go.mod h1:Byg39a10tXQ6ysRz5r59YhVMUKgXAThI+0/LcVr+WOE=
github.com/Yuzuki616/quic-go v0.34.1 h1:9Is+Dofzn6qJ9a9t4Ixe4oX7Cb4LU7u4zDozVB8DBDY=
github.com/Yuzuki616/quic-go v0.34.1/go.mod h1:089qZpsXn7CL8kE7G2HurF0bLiZnzQdRIrT7PM0MMQs=
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 h1:F1j7z+/DKEsYqZNoxC6wvfmaiDneLsQOFQmuq9NADSY=
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2/go.mod h1:QlXr/TrICfQ/ANa76sLeQyhAJyNR9sEcfNuZBkY9jgY=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 h1:J45/QHgrzUdqe/Vco/Vxk0wRvdS2nKUxmf/zLgvfass=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI=
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH
Download .txt
gitextract_ebfl6u56/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug-report.md
│   │   └── feature-request.md
│   ├── build/
│   │   └── friendly-filenames.json
│   ├── dependabot.yml
│   └── workflows/
│       ├── codeql-analysis.yml
│       └── release.yml
├── .gitignore
├── LICENSE
├── README.md
├── api/
│   ├── iprecoder/
│   │   ├── interface.go
│   │   ├── recorder.go
│   │   ├── redis.go
│   │   └── redis_test.go
│   └── panel/
│       ├── node.go
│       ├── node_test.go
│       ├── panel.go
│       ├── user.go
│       └── utils.go
├── cmd/
│   ├── action_linux.go
│   ├── cmd.go
│   ├── common.go
│   ├── common_test.go
│   ├── install_linux.go
│   ├── server.go
│   ├── server_test.go
│   ├── synctime.go
│   ├── version.go
│   ├── x25519.go
│   └── x25519_test.go
├── common/
│   ├── crypt/
│   │   ├── aes.go
│   │   └── x25519.go
│   ├── exec/
│   │   └── exec.go
│   ├── file/
│   │   └── file.go
│   ├── format/
│   │   └── user.go
│   ├── rate/
│   │   └── rate.go
│   ├── systime/
│   │   ├── time_stub.go
│   │   ├── time_unix.go
│   │   └── time_windows.go
│   └── task/
│       ├── task.go
│       └── task_test.go
├── conf/
│   ├── conf.go
│   ├── conf_test.go
│   ├── core.go
│   ├── log.go
│   ├── node.go
│   ├── old.go
│   └── watch.go
├── core/
│   ├── core.go
│   ├── hy/
│   │   ├── config.go
│   │   ├── counter.go
│   │   ├── counter_test.go
│   │   ├── hy.go
│   │   ├── ipmasker.go
│   │   ├── kploader.go
│   │   ├── mmdb.go
│   │   ├── node.go
│   │   ├── resolver.go
│   │   ├── server.go
│   │   ├── server_test.go
│   │   └── user.go
│   ├── imports/
│   │   ├── hy.go
│   │   ├── imports.go
│   │   └── xray.go
│   ├── interface.go
│   ├── selector.go
│   └── xray/
│       ├── app/
│       │   ├── app.go
│       │   └── dispatcher/
│       │       ├── config.pb.go
│       │       ├── config.proto
│       │       ├── default.go
│       │       ├── dispatcher.go
│       │       ├── errors.generated.go
│       │       ├── fakednssniffer.go
│       │       ├── sniffer.go
│       │       ├── stats.go
│       │       └── stats_test.go
│       ├── distro/
│       │   └── all/
│       │       └── all.go
│       ├── inbound.go
│       ├── node.go
│       ├── outbound.go
│       ├── ss.go
│       ├── trojan.go
│       ├── user.go
│       ├── vmess.go
│       └── xray.go
├── example/
│   ├── config.yml.example
│   ├── custom_inbound.json
│   ├── custom_outbound.json
│   ├── dns.json
│   ├── route.json
│   └── rulelist
├── go.mod
├── go.sum
├── limiter/
│   ├── clear.go
│   ├── conn.go
│   ├── conn_test.go
│   ├── dynamic.go
│   ├── limiter.go
│   └── rule.go
├── main.go
├── node/
│   ├── cert.go
│   ├── controller.go
│   ├── lego/
│   │   ├── cert.go
│   │   ├── lego.go
│   │   ├── lego_test.go
│   │   └── user.go
│   ├── node.go
│   ├── task.go
│   └── user.go
└── test_data/
    ├── 1.key
    └── 1.pem
Download .txt
SYMBOL INDEX (353 symbols across 84 files)

FILE: api/iprecoder/interface.go
  type IpRecorder (line 7) | type IpRecorder interface

FILE: api/iprecoder/recorder.go
  type Recorder (line 12) | type Recorder struct
    method SyncOnlineIp (line 24) | func (r *Recorder) SyncOnlineIp(ips []limiter.UserIpList) ([]limiter.U...
  function NewRecorder (line 17) | func NewRecorder(c *conf.RecorderConfig) *Recorder {

FILE: api/iprecoder/redis.go
  type Redis (line 13) | type Redis struct
    method SyncOnlineIp (line 29) | func (r *Redis) SyncOnlineIp(Ips []limiter.UserIpList) ([]limiter.User...
  function NewRedis (line 18) | func NewRedis(c *conf.RedisConfig) *Redis {

FILE: api/iprecoder/redis_test.go
  function TestRedis_SyncOnlineIp (line 10) | func TestRedis_SyncOnlineIp(t *testing.T) {

FILE: api/panel/node.go
  type CommonNodeRsp (line 19) | type CommonNodeRsp struct
  type Route (line 27) | type Route struct
  type BaseConfig (line 33) | type BaseConfig struct
  type V2rayNodeRsp (line 38) | type V2rayNodeRsp struct
  type ShadowsocksNodeRsp (line 45) | type ShadowsocksNodeRsp struct
  type HysteriaNodeRsp (line 50) | type HysteriaNodeRsp struct
  type NodeInfo (line 56) | type NodeInfo struct
  type Rules (line 76) | type Rules struct
  type V2rayExtraConfig (line 81) | type V2rayExtraConfig struct
  type RealityConfig (line 88) | type RealityConfig struct
  method GetNodeInfo (line 99) | func (c *Client) GetNodeInfo() (node *NodeInfo, err error) {
  function intervalToTime (line 234) | func intervalToTime(i interface{}) time.Duration {

FILE: api/panel/node_test.go
  function init (line 11) | func init() {
  function TestClient_GetNodeInfo (line 24) | func TestClient_GetNodeInfo(t *testing.T) {
  function TestClient_ReportUserTraffic (line 29) | func TestClient_ReportUserTraffic(t *testing.T) {

FILE: api/panel/panel.go
  type Client (line 19) | type Client struct
  function New (line 30) | func New(c *conf.ApiConfig) (*Client, error) {
  function readLocalRuleList (line 72) | func readLocalRuleList(path string) (LocalRuleList []*regexp.Regexp) {

FILE: api/panel/user.go
  type OnlineUser (line 9) | type OnlineUser struct
  type UserInfo (line 14) | type UserInfo struct
  type UserListBody (line 20) | type UserListBody struct
  method GetUserList (line 26) | func (c *Client) GetUserList() (UserList []UserInfo, err error) {
  type UserTraffic (line 48) | type UserTraffic struct
  method ReportUserTraffic (line 55) | func (c *Client) ReportUserTraffic(userTraffic []UserTraffic) error {

FILE: api/panel/utils.go
  method Debug (line 10) | func (c *Client) Debug() {
  method assembleURL (line 14) | func (c *Client) assembleURL(path string) string {
  method checkResponse (line 17) | func (c *Client) checkResponse(res *resty.Response, path string, err err...

FILE: cmd/action_linux.go
  function init (line 35) | func init() {
  function startHandle (line 42) | func startHandle(_ *cobra.Command, _ []string) {
  function stopHandle (line 71) | func stopHandle(_ *cobra.Command, _ []string) {
  function restartHandle (line 92) | func restartHandle(_ *cobra.Command, _ []string) {

FILE: cmd/cmd.go
  function Run (line 14) | func Run() {

FILE: cmd/common.go
  constant red (line 10) | red    = "\033[0;31m"
  constant green (line 11) | green  = "\033[0;32m"
  constant yellow (line 12) | yellow = "\033[0;33m"
  constant plain (line 13) | plain  = "\033[0m"
  function checkRunning (line 16) | func checkRunning() (bool, error) {
  function Err (line 24) | func Err(msg ...any) string {
  function Ok (line 28) | func Ok(msg ...any) string {
  function Warn (line 32) | func Warn(msg ...any) string {

FILE: cmd/common_test.go
  function Test_printFailed (line 5) | func Test_printFailed(t *testing.T) {

FILE: cmd/install_linux.go
  function init (line 31) | func init() {
  function uninstallHandle (line 37) | func uninstallHandle(_ *cobra.Command, _ []string) {

FILE: cmd/server.go
  function init (line 30) | func init() {
  function serverHandle (line 40) | func serverHandle(_ *cobra.Command, _ []string) {

FILE: cmd/server_test.go
  function TestRun (line 5) | func TestRun(t *testing.T) {

FILE: cmd/synctime.go
  function init (line 20) | func init() {
  function synctimeHandle (line 25) | func synctimeHandle(_ *cobra.Command, _ []string) {

FILE: cmd/version.go
  function init (line 25) | func init() {
  function showVersion (line 29) | func showVersion() {

FILE: cmd/x25519.go
  function init (line 23) | func init() {
  function executeX25519 (line 27) | func executeX25519() {

FILE: cmd/x25519_test.go
  function Test_executeX25519 (line 5) | func Test_executeX25519(t *testing.T) {

FILE: common/crypt/aes.go
  function AesEncrypt (line 8) | func AesEncrypt(data []byte, key []byte) (string, error) {
  function AesDecrypt (line 18) | func AesDecrypt(data string, key []byte) (string, error) {

FILE: common/crypt/x25519.go
  function GenX25519Private (line 7) | func GenX25519Private(data []byte) []byte {

FILE: common/exec/exec.go
  function RunCommandByShell (line 9) | func RunCommandByShell(cmd string) (string, error) {
  function RunCommandStd (line 19) | func RunCommandStd(name string, args ...string) {

FILE: common/file/file.go
  function IsExist (line 5) | func IsExist(path string) bool {

FILE: common/format/user.go
  function UserTag (line 7) | func UserTag(tag string, uuid string) string {

FILE: common/rate/rate.go
  type Writer (line 9) | type Writer struct
    method Close (line 21) | func (w *Writer) Close() error {
    method WriteMultiBuffer (line 25) | func (w *Writer) WriteMultiBuffer(mb buf.MultiBuffer) error {
  function NewRateLimitWriter (line 14) | func NewRateLimitWriter(writer buf.Writer, limiter *ratelimit.Bucket) bu...

FILE: common/systime/time_stub.go
  function SetSystemTime (line 10) | func SetSystemTime(nowTime time.Time) error {

FILE: common/systime/time_unix.go
  function SetSystemTime (line 11) | func SetSystemTime(nowTime time.Time) error {

FILE: common/systime/time_windows.go
  function SetSystemTime (line 10) | func SetSystemTime(nowTime time.Time) error {

FILE: common/task/task.go
  type Task (line 9) | type Task struct
    method hasClosed (line 20) | func (t *Task) hasClosed() bool {
    method checkedExecute (line 27) | func (t *Task) checkedExecute(first bool) error {
    method Start (line 51) | func (t *Task) Start(first bool) error {
    method Close (line 69) | func (t *Task) Close() {

FILE: common/task/task_test.go
  function TestTask (line 9) | func TestTask(t *testing.T) {

FILE: conf/conf.go
  type Conf (line 10) | type Conf struct
    method LoadFromPath (line 33) | func (p *Conf) LoadFromPath(filePath string) error {
  function New (line 15) | func New() *Conf {

FILE: conf/conf_test.go
  function TestConf_LoadFromPath (line 8) | func TestConf_LoadFromPath(t *testing.T) {
  function TestConf_Watch (line 13) | func TestConf_Watch(t *testing.T) {

FILE: conf/core.go
  type CoreConfig (line 3) | type CoreConfig struct
  type XrayConfig (line 8) | type XrayConfig struct
  type ConnectionConfig (line 18) | type ConnectionConfig struct
  function NewConnectionConfig (line 26) | func NewConnectionConfig() *ConnectionConfig {

FILE: conf/log.go
  type LogConfig (line 3) | type LogConfig struct
  function NewLogConfig (line 9) | func NewLogConfig() *LogConfig {

FILE: conf/node.go
  type NodeConfig (line 3) | type NodeConfig struct
  type ApiConfig (line 8) | type ApiConfig struct
  type ControllerConfig (line 17) | type ControllerConfig struct
  type RealityConfig (line 26) | type RealityConfig struct
  type XrayOptions (line 37) | type XrayOptions struct
  type HyOptions (line 49) | type HyOptions struct
  type LimitConfig (line 55) | type LimitConfig struct
  type FallBackConfig (line 66) | type FallBackConfig struct
  type RecorderConfig (line 74) | type RecorderConfig struct
  type RedisConfig (line 80) | type RedisConfig struct
  type IpReportConfig (line 87) | type IpReportConfig struct
  type DynamicSpeedLimitConfig (line 95) | type DynamicSpeedLimitConfig struct
  type CertConfig (line 102) | type CertConfig struct

FILE: conf/old.go
  type OldConfig (line 5) | type OldConfig struct
  type OldControllerConfig (line 12) | type OldControllerConfig struct
  type OldApiConfig (line 31) | type OldApiConfig struct
  function migrateOldConfig (line 44) | func migrateOldConfig(c *Conf, old *OldConfig) {

FILE: conf/watch.go
  method Watch (line 11) | func (p *Conf) Watch(filePath, dnsPath string, reload func()) error {

FILE: core/core.go
  function NewCore (line 14) | func NewCore(c *conf.CoreConfig) (Core, error) {
  function RegisterCore (line 41) | func RegisterCore(t string, f func(c *conf.CoreConfig) (Core, error)) {
  function RegisteredCore (line 45) | func RegisteredCore() []string {

FILE: core/hy/config.go
  constant mbpsToBps (line 4) | mbpsToBps   = 125000
  constant minSpeedBPS (line 5) | minSpeedBPS = 16384
  constant DefaultALPN (line 7) | DefaultALPN = "hysteria"
  constant DefaultStreamReceiveWindow (line 9) | DefaultStreamReceiveWindow     = 16777216
  constant DefaultConnectionReceiveWindow (line 10) | DefaultConnectionReceiveWindow = DefaultStreamReceiveWindow * 5 / 2
  constant DefaultMaxIncomingStreams (line 12) | DefaultMaxIncomingStreams = 1024
  constant DefaultMMDBFilename (line 14) | DefaultMMDBFilename = "GeoLite2-Country.mmdb"
  constant ServerMaxIdleTimeoutSec (line 16) | ServerMaxIdleTimeoutSec     = 60
  constant DefaultClientIdleTimeoutSec (line 17) | DefaultClientIdleTimeoutSec = 20
  constant DefaultClientHopIntervalSec (line 19) | DefaultClientHopIntervalSec = 10
  function SpeedTrans (line 22) | func SpeedTrans(upM, downM int) (uint64, uint64) {

FILE: core/hy/counter.go
  type UserTrafficCounter (line 8) | type UserTrafficCounter struct
    method getCounters (line 25) | func (c *UserTrafficCounter) getCounters(auth string) *counters {
    method Rx (line 36) | func (c *UserTrafficCounter) Rx(auth string, n int) {
    method Tx (line 41) | func (c *UserTrafficCounter) Tx(auth string, n int) {
    method IncConn (line 46) | func (c *UserTrafficCounter) IncConn(_ string) {
    method DecConn (line 52) | func (c *UserTrafficCounter) DecConn(_ string) {
    method Reset (line 58) | func (c *UserTrafficCounter) Reset(auth string) {
    method Delete (line 64) | func (c *UserTrafficCounter) Delete(auth string) {
  type counters (line 13) | type counters struct
  function NewUserTrafficCounter (line 19) | func NewUserTrafficCounter() *UserTrafficCounter {

FILE: core/hy/counter_test.go
  function TestUserTrafficCounter_Rx (line 5) | func TestUserTrafficCounter_Rx(t *testing.T) {

FILE: core/hy/hy.go
  function init (line 12) | func init() {
  type Hy (line 16) | type Hy struct
    method Start (line 26) | func (h *Hy) Start() error {
    method Close (line 30) | func (h *Hy) Close() error {
    method Protocols (line 45) | func (h *Hy) Protocols() []string {
  function NewHy (line 20) | func NewHy(_ *conf.CoreConfig) (vCore.Core, error) {

FILE: core/hy/ipmasker.go
  type ipMasker (line 7) | type ipMasker struct
    method Mask (line 14) | func (m *ipMasker) Mask(addr string) string {

FILE: core/hy/kploader.go
  type keypairLoader (line 11) | type keypairLoader struct
    method load (line 78) | func (kpr *keypairLoader) load() error {
    method GetCertificateFunc (line 89) | func (kpr *keypairLoader) GetCertificateFunc() func(*tls.ClientHelloIn...
  function newKeypairLoader (line 18) | func newKeypairLoader(certPath, keyPath string) (*keypairLoader, error) {

FILE: core/hy/mmdb.go
  function loadMMDBReader (line 10) | func loadMMDBReader(filename string) (*geoip2.Reader, error) {

FILE: core/hy/node.go
  method AddNode (line 11) | func (h *Hy) AddNode(tag string, info *panel.NodeInfo, c *conf.Controlle...
  method DelNode (line 32) | func (h *Hy) DelNode(tag string) error {

FILE: core/hy/resolver.go
  function setResolver (line 15) | func setResolver(dns string) error {

FILE: core/hy/server.go
  type Server (line 34) | type Server struct
    method runServer (line 50) | func (s *Server) runServer(node *panel.NodeInfo, c *conf.ControllerCon...
    method authByUser (line 162) | func (s *Server) authByUser(addr net.Addr, auth []byte, sSend uint64, ...
    method connectFunc (line 172) | func (s *Server) connectFunc(addr net.Addr, auth []byte, sSend uint64,...
    method disconnectFunc (line 189) | func (s *Server) disconnectFunc(addr net.Addr, auth []byte, err error) {
  function NewServer (line 43) | func NewServer(tag string, l *limiter.Limiter) *Server {
  function tcpRequestFunc (line 197) | func tcpRequestFunc(addr net.Addr, auth []byte, reqAddr string, action a...
  function tcpErrorFunc (line 205) | func tcpErrorFunc(addr net.Addr, auth []byte, reqAddr string, err error) {
  function udpRequestFunc (line 220) | func udpRequestFunc(addr net.Addr, auth []byte, sessionID uint32) {
  function udpErrorFunc (line 227) | func udpErrorFunc(addr net.Addr, auth []byte, sessionID uint32, err erro...
  function actionToString (line 242) | func actionToString(action acl.Action, arg string) string {

FILE: core/hy/server_test.go
  function TestServer (line 14) | func TestServer(t *testing.T) {

FILE: core/hy/user.go
  method AddUsers (line 10) | func (h *Hy) AddUsers(p *core.AddUsersParams) (int, error) {
  method GetUserTraffic (line 22) | func (h *Hy) GetUserTraffic(tag, uuid string, reset bool) (up int64, dow...
  method DelUsers (line 34) | func (h *Hy) DelUsers(users []panel.UserInfo, tag string) error {

FILE: core/interface.go
  type AddUsersParams (line 8) | type AddUsersParams struct
  type Core (line 14) | type Core interface

FILE: core/selector.go
  type Selector (line 12) | type Selector struct
    method Start (line 17) | func (s *Selector) Start() error {
    method Close (line 25) | func (s *Selector) Close() error {
    method AddNode (line 42) | func (s *Selector) AddNode(tag string, info *panel.NodeInfo, config *c...
    method DelNode (line 57) | func (s *Selector) DelNode(tag string) error {
    method AddUsers (line 69) | func (s *Selector) AddUsers(p *AddUsersParams) (added int, err error) {
    method GetUserTraffic (line 77) | func (s *Selector) GetUserTraffic(tag, uuid string, reset bool) (up in...
    method DelUsers (line 85) | func (s *Selector) DelUsers(users []panel.UserInfo, tag string) error {
    method Protocols (line 93) | func (s *Selector) Protocols() []string {
  function isSupported (line 33) | func isSupported(protocol string, protocols []string) bool {

FILE: core/xray/app/dispatcher/config.pb.go
  constant _ (line 18) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
  constant _ (line 20) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
  type SessionConfig (line 23) | type SessionConfig struct
    method Reset (line 29) | func (x *SessionConfig) Reset() {
    method String (line 38) | func (x *SessionConfig) String() string {
    method ProtoMessage (line 42) | func (*SessionConfig) ProtoMessage() {}
    method ProtoReflect (line 44) | func (x *SessionConfig) ProtoReflect() protoreflect.Message {
    method Descriptor (line 57) | func (*SessionConfig) Descriptor() ([]byte, []int) {
  type Config (line 61) | type Config struct
    method Reset (line 69) | func (x *Config) Reset() {
    method String (line 78) | func (x *Config) String() string {
    method ProtoMessage (line 82) | func (*Config) ProtoMessage() {}
    method ProtoReflect (line 84) | func (x *Config) ProtoReflect() protoreflect.Message {
    method Descriptor (line 97) | func (*Config) Descriptor() ([]byte, []int) {
    method GetSettings (line 101) | func (x *Config) GetSettings() *SessionConfig {
  function file_config_proto_rawDescGZIP (line 135) | func file_config_proto_rawDescGZIP() []byte {
  function init (line 156) | func init() { file_config_proto_init() }
  function file_config_proto_init (line 157) | func file_config_proto_init() {

FILE: core/xray/app/dispatcher/default.go
  type cachedReader (line 34) | type cachedReader struct
    method Cache (line 40) | func (r *cachedReader) Cache(b *buf.Buffer) {
    method readInternal (line 53) | func (r *cachedReader) readInternal() buf.MultiBuffer {
    method ReadMultiBuffer (line 66) | func (r *cachedReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
    method ReadMultiBufferTimeout (line 75) | func (r *cachedReader) ReadMultiBufferTimeout(timeout time.Duration) (...
    method Interrupt (line 84) | func (r *cachedReader) Interrupt() {
  type DefaultDispatcher (line 94) | type DefaultDispatcher struct
    method Init (line 119) | func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, ...
    method Type (line 129) | func (*DefaultDispatcher) Type() interface{} {
    method Start (line 134) | func (*DefaultDispatcher) Start() error {
    method Close (line 139) | func (*DefaultDispatcher) Close() error { return nil }
    method getLink (line 141) | func (d *DefaultDispatcher) getLink(ctx context.Context, network net.N...
    method shouldOverride (line 280) | func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result...
    method Dispatch (line 314) | func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination ...
    method DispatchLink (line 367) | func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destinat...
    method routedDispatch (line 493) | func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *...
  function init (line 103) | func init() {
  type limitedError (line 415) | type limitedError
    method Error (line 417) | func (l limitedError) Error() string {
  function sniffer (line 421) | func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bo...

FILE: core/xray/app/dispatcher/errors.generated.go
  type errPathObjHolder (line 5) | type errPathObjHolder struct
  function newError (line 7) | func newError(values ...interface{}) *errors.Error {

FILE: core/xray/app/dispatcher/fakednssniffer.go
  function newFakeDNSSniffer (line 15) | func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata...
  type fakeDNSSniffResult (line 50) | type fakeDNSSniffResult struct
    method Protocol (line 54) | func (fakeDNSSniffResult) Protocol() string {
    method Domain (line 58) | func (f fakeDNSSniffResult) Domain() string {
  type fakeDNSExtraOpts (line 62) | type fakeDNSExtraOpts
  constant ipAddressInRange (line 64) | ipAddressInRange fakeDNSExtraOpts = 1
  type ipAddressInRangeOpt (line 66) | type ipAddressInRangeOpt struct
  type DNSThenOthersSniffResult (line 70) | type DNSThenOthersSniffResult struct
    method IsProtoSubsetOf (line 75) | func (f DNSThenOthersSniffResult) IsProtoSubsetOf(protocolName string)...
    method Protocol (line 79) | func (DNSThenOthersSniffResult) Protocol() string {
    method Domain (line 83) | func (f DNSThenOthersSniffResult) Domain() string {
  function newFakeDNSThenOthers (line 87) | func newFakeDNSThenOthers(ctx context.Context, fakeDNSSniffer protocolSn...

FILE: core/xray/app/dispatcher/sniffer.go
  type SniffResult (line 14) | type SniffResult interface
  type protocolSniffer (line 19) | type protocolSniffer
  type protocolSnifferWithMetadata (line 21) | type protocolSnifferWithMetadata struct
  type Sniffer (line 30) | type Sniffer struct
    method Sniff (line 57) | func (s *Sniffer) Sniff(c context.Context, payload []byte, network net...
    method SniffMetadata (line 83) | func (s *Sniffer) SniffMetadata(c context.Context) (SniffResult, error) {
  function NewSniffer (line 34) | func NewSniffer(ctx context.Context) *Sniffer {
  function CompositeResult (line 110) | func CompositeResult(domainResult SniffResult, protocolResult SniffResul...
  type compositeResult (line 114) | type compositeResult struct
    method Protocol (line 119) | func (c compositeResult) Protocol() string {
    method Domain (line 123) | func (c compositeResult) Domain() string {
    method ProtocolForDomainResult (line 127) | func (c compositeResult) ProtocolForDomainResult() string {
  type SnifferResultComposite (line 131) | type SnifferResultComposite interface
  type SnifferIsProtoSubsetOf (line 135) | type SnifferIsProtoSubsetOf interface

FILE: core/xray/app/dispatcher/stats.go
  type SizeStatWriter (line 9) | type SizeStatWriter struct
    method WriteMultiBuffer (line 14) | func (w *SizeStatWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
    method Close (line 19) | func (w *SizeStatWriter) Close() error {
    method Interrupt (line 23) | func (w *SizeStatWriter) Interrupt() {

FILE: core/xray/app/dispatcher/stats_test.go
  type TestCounter (line 11) | type TestCounter
    method Value (line 13) | func (c *TestCounter) Value() int64 {
    method Add (line 17) | func (c *TestCounter) Add(v int64) int64 {
    method Set (line 23) | func (c *TestCounter) Set(v int64) int64 {
  function TestStatsWriter (line 28) | func TestStatsWriter(t *testing.T) {

FILE: core/xray/inbound.go
  function buildInbound (line 20) | func buildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInf...
  function buildV2ray (line 153) | func buildV2ray(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo,...
  function buildTrojan (line 216) | func buildTrojan(config *conf.ControllerConfig, inbound *coreConf.Inboun...
  function buildShadowsocks (line 240) | func buildShadowsocks(config *conf.ControllerConfig, nodeInfo *panel.Nod...
  function buildVlessFallbacks (line 277) | func buildVlessFallbacks(fallbackConfigs []conf.FallBackConfig) ([]*core...
  function buildTrojanFallbacks (line 302) | func buildTrojanFallbacks(fallbackConfigs []conf.FallBackConfig) ([]*cor...

FILE: core/xray/node.go
  method AddNode (line 13) | func (c *Core) AddNode(tag string, info *panel.NodeInfo, config *conf.Co...
  method addInbound (line 33) | func (c *Core) addInbound(config *core.InboundHandlerConfig) error {
  method addOutbound (line 48) | func (c *Core) addOutbound(config *core.OutboundHandlerConfig) error {
  method DelNode (line 63) | func (c *Core) DelNode(tag string) error {
  method removeInbound (line 75) | func (c *Core) removeInbound(tag string) error {
  method removeOutbound (line 79) | func (c *Core) removeOutbound(tag string) error {

FILE: core/xray/outbound.go
  function buildOutbound (line 13) | func buildOutbound(config *conf2.ControllerConfig, tag string) (*core.Ou...

FILE: core/xray/ss.go
  function buildSSUsers (line 14) | func buildSSUsers(tag string, userInfo []panel.UserInfo, cypher string, ...
  function buildSSUser (line 22) | func buildSSUser(tag string, userInfo *panel.UserInfo, cypher string, se...
  function getCipherFromString (line 52) | func getCipherFromString(c string) shadowsocks.CipherType {

FILE: core/xray/trojan.go
  function buildTrojanUsers (line 11) | func buildTrojanUsers(tag string, userInfo []panel.UserInfo) (users []*p...
  function buildTrojanUser (line 19) | func buildTrojanUser(tag string, userInfo *panel.UserInfo) (user *protoc...

FILE: core/xray/user.go
  method GetUserManager (line 14) | func (c *Core) GetUserManager(tag string) (proxy.UserManager, error) {
  method DelUsers (line 30) | func (c *Core) DelUsers(users []panel.UserInfo, tag string) error {
  method GetUserTraffic (line 50) | func (c *Core) GetUserTraffic(tag, uuid string, reset bool) (up int64, d...
  method AddUsers (line 73) | func (c *Core) AddUsers(p *vCore.AddUsersParams) (added int, err error) {

FILE: core/xray/vmess.go
  function buildVmessUsers (line 12) | func buildVmessUsers(tag string, userInfo []panel.UserInfo) (users []*pr...
  function buildVmessUser (line 20) | func buildVmessUser(tag string, userInfo *panel.UserInfo) (user *protoco...
  function buildVlessUsers (line 32) | func buildVlessUsers(tag string, userInfo []panel.UserInfo, flow string)...
  function buildVlessUser (line 40) | func buildVlessUser(tag string, userInfo *panel.UserInfo, flow string) (...

FILE: core/xray/xray.go
  function init (line 24) | func init() {
  type Core (line 29) | type Core struct
    method Start (line 161) | func (c *Core) Start() error {
    method Close (line 175) | func (c *Core) Close() error {
    method Protocols (line 189) | func (c *Core) Protocols() []string {
  function New (line 38) | func New(c *conf.CoreConfig) (vCore.Core, error) {
  function parseConnectionConfig (line 42) | func parseConnectionConfig(c *conf.ConnectionConfig) (policy *coreConf.P...
  function getCore (line 55) | func getCore(c *conf.XrayConfig) *core.Instance {

FILE: limiter/clear.go
  function ClearOnlineIP (line 5) | func ClearOnlineIP() error {

FILE: limiter/conn.go
  type ConnLimiter (line 8) | type ConnLimiter struct
    method AddConnCount (line 26) | func (c *ConnLimiter) AddConnCount(user string, ip string, isTcp bool)...
    method DelConnCount (line 102) | func (c *ConnLimiter) DelConnCount(user string, ip string) {
    method ClearOnlineIP (line 139) | func (c *ConnLimiter) ClearOnlineIP() {
  function NewConnLimiter (line 16) | func NewConnLimiter(conn int, ip int, realtime bool) *ConnLimiter {

FILE: limiter/conn_test.go
  function init (line 11) | func init() {
  function TestConnLimiter_AddConnCount (line 15) | func TestConnLimiter_AddConnCount(t *testing.T) {
  function TestConnLimiter_DelConnCount (line 20) | func TestConnLimiter_DelConnCount(t *testing.T) {
  function TestConnLimiter_ClearOnlineIP (line 27) | func TestConnLimiter_ClearOnlineIP(t *testing.T) {
  function BenchmarkConnLimiter (line 44) | func BenchmarkConnLimiter(b *testing.B) {

FILE: limiter/dynamic.go
  method AddDynamicSpeedLimit (line 9) | func (l *Limiter) AddDynamicSpeedLimit(tag string, userInfo *panel.UserI...
  function determineSpeedLimit (line 19) | func determineSpeedLimit(limit1, limit2 int) (limit int) {

FILE: limiter/limiter.go
  function Init (line 20) | func Init() {
  type Limiter (line 34) | type Limiter struct
    method UpdateUser (line 88) | func (l *Limiter) UpdateUser(tag string, added []panel.UserInfo, delet...
    method UpdateDynamicSpeedLimit (line 104) | func (l *Limiter) UpdateDynamicSpeedLimit(tag, uuid string, limit int,...
    method CheckLimit (line 115) | func (l *Limiter) CheckLimit(email string, ip string, isTcp bool) (Buc...
  type UserLimitInfo (line 43) | type UserLimitInfo struct
  function AddLimiter (line 50) | func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo)...
  function GetLimiter (line 72) | func GetLimiter(tag string) (info *Limiter, err error) {
  function DeleteLimiter (line 82) | func DeleteLimiter(tag string) {
  type UserIpList (line 151) | type UserIpList struct
  function determineDeviceLimit (line 156) | func determineDeviceLimit(nodeLimit, userLimit int) (limit int) {

FILE: limiter/rule.go
  method CheckDomainRule (line 9) | func (l *Limiter) CheckDomainRule(destination string) (reject bool) {
  method CheckProtocolRule (line 20) | func (l *Limiter) CheckProtocolRule(protocol string) (reject bool) {
  method UpdateRule (line 30) | func (l *Limiter) UpdateRule(rule *panel.Rules) error {

FILE: main.go
  function main (line 5) | func main() {

FILE: node/cert.go
  method renewCertTask (line 10) | func (c *Controller) renewCertTask() error {
  method requestCert (line 23) | func (c *Controller) requestCert() error {

FILE: node/controller.go
  type Controller (line 16) | type Controller struct
    method Start (line 44) | func (c *Controller) Start() error {
    method Close (line 95) | func (c *Controller) Close() error {
    method buildNodeTag (line 115) | func (c *Controller) buildNodeTag(node *panel.NodeInfo) string {
  function NewController (line 34) | func NewController(server vCore.Core, api *panel.Client, config *conf.Co...

FILE: node/lego/cert.go
  method SetProvider (line 14) | func (l *Lego) SetProvider() error {
  method CreateCert (line 37) | func (l *Lego) CreateCert() (err error) {
  method RenewCert (line 49) | func (l *Lego) RenewCert() error {
  method CheckCert (line 70) | func (l *Lego) CheckCert(file []byte) (bool, error) {
  method parseParams (line 81) | func (l *Lego) parseParams(path string) string {
  method writeCert (line 86) | func (l *Lego) writeCert(certificates *certificate.Resource) error {

FILE: node/lego/lego.go
  type Lego (line 13) | type Lego struct
  function New (line 18) | func New(config *conf.CertConfig) (*Lego, error) {
  function checkPath (line 44) | func checkPath(p string) error {

FILE: node/lego/lego_test.go
  function init (line 12) | func init() {
  function TestLego_CreateCertByDns (line 31) | func TestLego_CreateCertByDns(t *testing.T) {
  function TestLego_RenewCert (line 38) | func TestLego_RenewCert(t *testing.T) {

FILE: node/lego/user.go
  type User (line 18) | type User struct
    method GetEmail (line 25) | func (u *User) GetEmail() string {
    method GetRegistration (line 28) | func (u *User) GetRegistration() *registration.Resource {
    method GetPrivateKey (line 31) | func (u *User) GetPrivateKey() crypto.PrivateKey {
    method Save (line 91) | func (u *User) Save(path string) error {
    method DecodePrivate (line 109) | func (u *User) DecodePrivate(pemEncodedPriv string) (*ecdsa.PrivateKey...
    method Load (line 115) | func (u *User) Load(path string) error {
  function NewUser (line 35) | func NewUser(path string, email string) (*User, error) {
  function registerUser (line 60) | func registerUser(user *User, path string) error {
  function EncodePrivate (line 83) | func EncodePrivate(privKey *ecdsa.PrivateKey) (string, error) {

FILE: node/node.go
  type Node (line 10) | type Node struct
    method Start (line 18) | func (n *Node) Start(nodes []*conf.NodeConfig, core vCore.Core) error {
    method Close (line 39) | func (n *Node) Close() {
  function New (line 14) | func New() *Node {

FILE: node/task.go
  method startTasks (line 13) | func (c *Controller) startTasks(node *panel.NodeInfo) {
  method nodeInfoMonitor (line 51) | func (c *Controller) nodeInfoMonitor() (err error) {
  method SpeedChecker (line 209) | func (c *Controller) SpeedChecker() error {

FILE: node/user.go
  method reportUserTrafficTask (line 11) | func (c *Controller) reportUserTrafficTask() (err error) {
  function compareUserList (line 46) | func compareUserList(old, new []panel.UserInfo) (deleted, added []panel....
Condensed preview — 110 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (350K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.md",
    "chars": 341,
    "preview": "---\nname: \"Bug 反馈\"\nabout: 创建一个报告以帮助我们修复并改进XrayR\ntitle: ''\nlabels: awaiting reply, bug\nassignees: ''\n---\n\n**描述该错误**\n简单地描述"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.md",
    "chars": 212,
    "preview": "---\nname: \"功能建议\"\nabout: 给XrayR提出建议,让我们做得更好\ntitle: ''\nlabels: awaiting reply, feature-request\nassignees: ''\n---\n\n**描述您想要的"
  },
  {
    "path": ".github/build/friendly-filenames.json",
    "chars": 1796,
    "preview": "{\n    \"android-arm64\": { \"friendlyName\": \"android-arm64-v8a\" },\n    \"darwin-amd64\": { \"friendlyName\": \"macos-64\" },\n    "
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 503,
    "preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "chars": 2346,
    "preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 6624,
    "preview": "name: Build and Release\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - master\n    paths:\n      - \"**/*.go\"\n    "
  },
  {
    "path": ".gitignore",
    "chars": 246,
    "preview": "example/config.yml\nexample/main\nexample/XrayR\nexample/XrayR*\nexample/mytest\nexample/access.logo\nexample/error.log\napi/ch"
  },
  {
    "path": "LICENSE",
    "chars": 16725,
    "preview": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\""
  },
  {
    "path": "README.md",
    "chars": 2409,
    "preview": "# V2bX\n**项目转移至[V2bX](https://github.com/InazumaV/V2bX),此仓库不再维护**\n\n[![](https://img.shields.io/badge/TgChat-%E4%BA%A4%E6%"
  },
  {
    "path": "api/iprecoder/interface.go",
    "chars": 168,
    "preview": "package iprecoder\n\nimport (\n\t\"github.com/Yuzuki616/V2bX/limiter\"\n)\n\ntype IpRecorder interface {\n\tSyncOnlineIp(Ips []limi"
  },
  {
    "path": "api/iprecoder/recorder.go",
    "chars": 857,
    "preview": "package iprecoder\n\nimport (\n\t\"errors\"\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\t\"github.com/Yuzuki616/V2bX/limiter\"\n\t\"github.co"
  },
  {
    "path": "api/iprecoder/redis.go",
    "chars": 1611,
    "preview": "package iprecoder\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\t\"github.com/Yuzuki616/V2bX/limiter\"\n\t\"g"
  },
  {
    "path": "api/iprecoder/redis_test.go",
    "chars": 415,
    "preview": "package iprecoder\n\nimport (\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\t\"github.com/Yuzuki616/V2bX/limiter\"\n\t\"log\"\n\t\"testing\"\n)\n\n"
  },
  {
    "path": "api/panel/node.go",
    "chars": 7087,
    "preview": "package panel\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"os\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/"
  },
  {
    "path": "api/panel/node_test.go",
    "chars": 584,
    "preview": "package panel\n\nimport (\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\t\"log\"\n\t\"testing\"\n)\n\nvar client *Client\n\nfunc init() {\n\tc, err"
  },
  {
    "path": "api/panel/panel.go",
    "chars": 2240,
    "preview": "package panel\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/Yuzuki616/V2b"
  },
  {
    "path": "api/panel/user.go",
    "chars": 1530,
    "preview": "package panel\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/goccy/go-json\"\n)\n\ntype OnlineUser struct {\n\tUID int\n\tIP  string\n}\n\ntype Use"
  },
  {
    "path": "api/panel/utils.go",
    "chars": 589,
    "preview": "package panel\n\nimport (\n\t\"fmt\"\n\t\"github.com/go-resty/resty/v2\"\n\tpath2 \"path\"\n)\n\n// Debug set the client debug for client"
  },
  {
    "path": "cmd/action_linux.go",
    "chars": 2462,
    "preview": "package cmd\n\nimport (\n\t\"fmt\"\n\t\"github.com/Yuzuki616/V2bX/common/exec\"\n\t\"github.com/spf13/cobra\"\n\t\"time\"\n)\n\nvar (\n\tstartC"
  },
  {
    "path": "cmd/cmd.go",
    "chars": 298,
    "preview": "package cmd\n\nimport (\n\tlog \"github.com/sirupsen/logrus\"\n\n\t_ \"github.com/Yuzuki616/V2bX/core/imports\"\n\t\"github.com/spf13/"
  },
  {
    "path": "cmd/common.go",
    "chars": 606,
    "preview": "package cmd\n\nimport (\n\t\"fmt\"\n\t\"github.com/Yuzuki616/V2bX/common/exec\"\n\t\"strings\"\n)\n\nconst (\n\tred    = \"\\033[0;31m\"\n\tgree"
  },
  {
    "path": "cmd/common_test.go",
    "chars": 91,
    "preview": "package cmd\n\nimport \"testing\"\n\nfunc Test_printFailed(t *testing.T) {\n\tt.Log(Err(\"test\"))\n}\n"
  },
  {
    "path": "cmd/install_linux.go",
    "chars": 1488,
    "preview": "package cmd\n\nimport (\n\t\"fmt\"\n\t\"github.com/Yuzuki616/V2bX/common/exec\"\n\t\"github.com/spf13/cobra\"\n\t\"os\"\n\t\"strings\"\n)\n\nvar "
  },
  {
    "path": "cmd/server.go",
    "chars": 2235,
    "preview": "package cmd\n\nimport (\n\tlog \"github.com/sirupsen/logrus\"\n\t\"os\"\n\t\"os/signal\"\n\t\"runtime\"\n\t\"syscall\"\n\n\tvCore \"github.com/Yuz"
  },
  {
    "path": "cmd/server_test.go",
    "chars": 69,
    "preview": "package cmd\n\nimport \"testing\"\n\nfunc TestRun(t *testing.T) {\n\tRun()\n}\n"
  },
  {
    "path": "cmd/synctime.go",
    "chars": 804,
    "preview": "package cmd\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/Yuzuki616/V2bX/common/systime\"\n\t\"github.com/beevik/ntp\"\n\t\"github.com/spf13/co"
  },
  {
    "path": "cmd/version.go",
    "chars": 774,
    "preview": "package cmd\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\tvCore \"github.com/Yuzuki616/V2bX/core\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar (\n\tver"
  },
  {
    "path": "cmd/x25519.go",
    "chars": 1406,
    "preview": "package cmd\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/Yuzuki616/V2bX/common/crypt\"\n\n\t\""
  },
  {
    "path": "cmd/x25519_test.go",
    "chars": 90,
    "preview": "package cmd\n\nimport \"testing\"\n\nfunc Test_executeX25519(t *testing.T) {\n\texecuteX25519()\n}\n"
  },
  {
    "path": "common/crypt/aes.go",
    "chars": 579,
    "preview": "package crypt\n\nimport (\n\t\"crypto/aes\"\n\t\"encoding/base64\"\n)\n\nfunc AesEncrypt(data []byte, key []byte) (string, error) {\n\t"
  },
  {
    "path": "common/crypt/x25519.go",
    "chars": 181,
    "preview": "package crypt\n\nimport (\n\t\"crypto/sha256\"\n)\n\nfunc GenX25519Private(data []byte) []byte {\n\tkey := sha256.Sum256(data)\n\tkey"
  },
  {
    "path": "common/exec/exec.go",
    "chars": 483,
    "preview": "package exec\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"os/exec\"\n)\n\nfunc RunCommandByShell(cmd string) (string, error) {\n\te := exec.Com"
  },
  {
    "path": "common/file/file.go",
    "chars": 129,
    "preview": "package file\n\nimport \"os\"\n\nfunc IsExist(path string) bool {\n\t_, err := os.Stat(path)\n\treturn err == nil || !os.IsNotExis"
  },
  {
    "path": "common/format/user.go",
    "chars": 124,
    "preview": "package format\n\nimport (\n\t\"fmt\"\n)\n\nfunc UserTag(tag string, uuid string) string {\n\treturn fmt.Sprintf(\"%s|%s\", tag, uuid"
  },
  {
    "path": "common/rate/rate.go",
    "chars": 549,
    "preview": "package rate\n\nimport (\n\t\"github.com/juju/ratelimit\"\n\t\"github.com/xtls/xray-core/common\"\n\t\"github.com/xtls/xray-core/comm"
  },
  {
    "path": "common/systime/time_stub.go",
    "chars": 155,
    "preview": "//go:build !(windows || linux || darwin)\n\npackage systime\n\nimport (\n\t\"os\"\n\t\"time\"\n)\n\nfunc SetSystemTime(nowTime time.Tim"
  },
  {
    "path": "common/systime/time_unix.go",
    "chars": 226,
    "preview": "//go:build linux || darwin\n\npackage systime\n\nimport (\n\t\"time\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc SetSystemTime(nowTime ti"
  },
  {
    "path": "common/systime/time_windows.go",
    "chars": 774,
    "preview": "package systime\n\nimport (\n\t\"time\"\n\t\"unsafe\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\nfunc SetSystemTime(nowTime time.Time) error "
  },
  {
    "path": "common/task/task.go",
    "chars": 1226,
    "preview": "package task\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\n// Task is a task that runs periodically.\ntype Task struct {\n\t// Interval of t"
  },
  {
    "path": "common/task/task_test.go",
    "chars": 196,
    "preview": "package task\n\nimport (\n\t\"log\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestTask(t *testing.T) {\n\tts := Task{Execute: func() error {\n\t\t"
  },
  {
    "path": "conf/conf.go",
    "chars": 1064,
    "preview": "package conf\n\nimport (\n\t\"fmt\"\n\t\"gopkg.in/yaml.v3\"\n\t\"io\"\n\t\"os\"\n)\n\ntype Conf struct {\n\tCoreConfig  CoreConfig    `yaml:\"Co"
  },
  {
    "path": "conf/conf_test.go",
    "chars": 304,
    "preview": "package conf\n\nimport (\n\t\"log\"\n\t\"testing\"\n)\n\nfunc TestConf_LoadFromPath(t *testing.T) {\n\tc := New()\n\tt.Log(c.LoadFromPath"
  },
  {
    "path": "conf/core.go",
    "chars": 993,
    "preview": "package conf\n\ntype CoreConfig struct {\n\tType       string      `yaml:\"Type\"`\n\tXrayConfig *XrayConfig `yaml:\"XrayConfig\"`"
  },
  {
    "path": "conf/log.go",
    "chars": 271,
    "preview": "package conf\n\ntype LogConfig struct {\n\tLevel      string `yaml:\"Level\"`\n\tAccessPath string `yaml:\"AccessPath\"`\n\tErrorPat"
  },
  {
    "path": "conf/node.go",
    "chars": 4208,
    "preview": "package conf\n\ntype NodeConfig struct {\n\tApiConfig        *ApiConfig        `yaml:\"ApiConfig\"`\n\tControllerConfig *Control"
  },
  {
    "path": "conf/old.go",
    "chars": 3229,
    "preview": "package conf\n\nimport \"log\"\n\ntype OldConfig struct {\n\tNodesConfig []*struct {\n\t\tApiConfig        *OldApiConfig        `ya"
  },
  {
    "path": "conf/watch.go",
    "chars": 1300,
    "preview": "package conf\n\nimport (\n\t\"fmt\"\n\t\"github.com/fsnotify/fsnotify\"\n\t\"log\"\n\t\"path\"\n\t\"time\"\n)\n\nfunc (p *Conf) Watch(filePath, d"
  },
  {
    "path": "core/core.go",
    "chars": 928,
    "preview": "package core\n\nimport (\n\t\"errors\"\n\t\"strings\"\n\n\t\"github.com/Yuzuki616/V2bX/conf\"\n)\n\nvar (\n\tcores = map[string]func(c *conf"
  },
  {
    "path": "core/hy/config.go",
    "chars": 573,
    "preview": "package hy\n\nconst (\n\tmbpsToBps   = 125000\n\tminSpeedBPS = 16384\n\n\tDefaultALPN = \"hysteria\"\n\n\tDefaultStreamReceiveWindow  "
  },
  {
    "path": "core/hy/counter.go",
    "chars": 1267,
    "preview": "package hy\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\ntype UserTrafficCounter struct {\n\tcounters map[string]*counters\n\tlock    "
  },
  {
    "path": "core/hy/counter_test.go",
    "chars": 80,
    "preview": "package hy\n\nimport \"testing\"\n\nfunc TestUserTrafficCounter_Rx(t *testing.T) {\n\n}\n"
  },
  {
    "path": "core/hy/hy.go",
    "chars": 740,
    "preview": "package hy\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\tvCore \"github.com/Yuzuki616/V2bX/core\"\n\t\"github."
  },
  {
    "path": "core/hy/ipmasker.go",
    "chars": 836,
    "preview": "package hy\n\nimport (\n\t\"net\"\n)\n\ntype ipMasker struct {\n\tIPv4Mask net.IPMask\n\tIPv6Mask net.IPMask\n}\n\n// Mask masks an addr"
  },
  {
    "path": "core/hy/kploader.go",
    "chars": 2097,
    "preview": "package hy\n\nimport (\n\t\"crypto/tls\"\n\t\"sync\"\n\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/sirupsen/logrus\"\n)\n\ntype keypai"
  },
  {
    "path": "core/hy/mmdb.go",
    "chars": 562,
    "preview": "package hy\n\nimport (\n\t\"os\"\n\n\t\"github.com/oschwald/geoip2-golang\"\n\t\"github.com/sirupsen/logrus\"\n)\n\nfunc loadMMDBReader(fi"
  },
  {
    "path": "core/hy/node.go",
    "chars": 944,
    "preview": "package hy\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\t\"github."
  },
  {
    "path": "core/hy/resolver.go",
    "chars": 3142,
    "preview": "package hy\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"github.com/Yuzuki616/hysteria/core/utils\"\n\trdns \"github.com/folbricht/rou"
  },
  {
    "path": "core/hy/server.go",
    "chars": 7664,
    "preview": "package hy\n\nimport (\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/Yuzuki616/V2bX/api/p"
  },
  {
    "path": "core/hy/server_test.go",
    "chars": 961,
    "preview": "package hy\n\nimport (\n\t\"encoding/base64\"\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\t\"githu"
  },
  {
    "path": "core/hy/user.go",
    "chars": 1069,
    "preview": "package hy\n\nimport (\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/cor"
  },
  {
    "path": "core/imports/hy.go",
    "chars": 95,
    "preview": "//go:build hy\n\npackage imports\n\n// not yet tested\nimport _ \"github.com/Yuzuki616/V2bX/core/hy\"\n"
  },
  {
    "path": "core/imports/imports.go",
    "chars": 16,
    "preview": "package imports\n"
  },
  {
    "path": "core/imports/xray.go",
    "chars": 81,
    "preview": "//go:build xray\n\npackage imports\n\nimport _ \"github.com/Yuzuki616/V2bX/core/xray\"\n"
  },
  {
    "path": "core/interface.go",
    "chars": 589,
    "preview": "package core\n\nimport (\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/conf\"\n)\n\ntype AddUsersParams s"
  },
  {
    "path": "core/selector.go",
    "chars": 2045,
    "preview": "package core\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\t\"git"
  },
  {
    "path": "core/xray/app/app.go",
    "chars": 101,
    "preview": "// Package app contains the third-party app used to replace the default app in xray-core\npackage app\n"
  },
  {
    "path": "core/xray/app/dispatcher/config.pb.go",
    "chars": 6504,
    "preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.28.1\n// \tprotoc        v3.21.12\n// so"
  },
  {
    "path": "core/xray/app/dispatcher/config.proto",
    "chars": 362,
    "preview": "syntax = \"proto3\";\n\npackage v2bx.core.app.dispatcher;\noption csharp_namespace = \"V2bX.core.app.dispatcher\";\noption go_pa"
  },
  {
    "path": "core/xray/app/dispatcher/default.go",
    "chars": 17794,
    "preview": "package dispatcher\n\n//go:generate go run github.com/xtls/xray-core/common/errors/errorgen\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\""
  },
  {
    "path": "core/xray/app/dispatcher/dispatcher.go",
    "chars": 90,
    "preview": "package dispatcher\n\n//go:generate go run github.com/xtls/xray-core/common/errors/errorgen\n"
  },
  {
    "path": "core/xray/app/dispatcher/errors.generated.go",
    "chars": 219,
    "preview": "package dispatcher\n\nimport \"github.com/xtls/xray-core/common/errors\"\n\ntype errPathObjHolder struct{}\n\nfunc newError(valu"
  },
  {
    "path": "core/xray/app/dispatcher/fakednssniffer.go",
    "chars": 3802,
    "preview": "package dispatcher\n\nimport (\n\t\"context\"\n\t\"strings\"\n\n\t\"github.com/xtls/xray-core/common\"\n\t\"github.com/xtls/xray-core/comm"
  },
  {
    "path": "core/xray/app/dispatcher/sniffer.go",
    "chars": 3837,
    "preview": "package dispatcher\n\nimport (\n\t\"context\"\n\n\t\"github.com/xtls/xray-core/common\"\n\t\"github.com/xtls/xray-core/common/net\"\n\t\"g"
  },
  {
    "path": "core/xray/app/dispatcher/stats.go",
    "chars": 514,
    "preview": "package dispatcher\n\nimport (\n\t\"github.com/xtls/xray-core/common\"\n\t\"github.com/xtls/xray-core/common/buf\"\n\t\"github.com/xt"
  },
  {
    "path": "core/xray/app/dispatcher/stats_test.go",
    "chars": 818,
    "preview": "package dispatcher_test\n\nimport (\n\t\"testing\"\n\n\t. \"github.com/xtls/xray-core/app/dispatcher\"\n\t\"github.com/xtls/xray-core/"
  },
  {
    "path": "core/xray/distro/all/all.go",
    "chars": 3022,
    "preview": "package all\n\nimport (\n\t// The following are necessary as they register handlers in their init functions.\n\n\t// Mandatory "
  },
  {
    "path": "core/xray/inbound.go",
    "chars": 10195,
    "preview": "package xray\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"github.com/Yuzuk"
  },
  {
    "path": "core/xray/node.go",
    "chars": 2049,
    "preview": "package xray\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\t\"gith"
  },
  {
    "path": "core/xray/outbound.go",
    "chars": 1222,
    "preview": "package xray\n\nimport (\n\t\"fmt\"\n\tconf2 \"github.com/Yuzuki616/V2bX/conf\"\n\t\"github.com/goccy/go-json\"\n\t\"github.com/xtls/xray"
  },
  {
    "path": "core/xray/ss.go",
    "chars": 1910,
    "preview": "package xray\n\nimport (\n\t\"encoding/base64\"\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/common/form"
  },
  {
    "path": "core/xray/trojan.go",
    "chars": 757,
    "preview": "package xray\n\nimport (\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/common/format\"\n\t\"github.com/xt"
  },
  {
    "path": "core/xray/user.go",
    "chars": 2880,
    "preview": "package xray\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/common/form"
  },
  {
    "path": "core/xray/vmess.go",
    "chars": 1410,
    "preview": "package xray\n\nimport (\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/common/format\"\n\t\"github.com/xt"
  },
  {
    "path": "core/xray/xray.go",
    "chars": 6240,
    "preview": "package xray\n\nimport (\n\t\"os\"\n\t\"sync\"\n\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\tvCore \"github.com/Yuzuki616/V2bX/core\"\n\t\"github"
  },
  {
    "path": "example/config.yml.example",
    "chars": 6441,
    "preview": "CoreConfig:\n  Type: \"xray\" # Core type, default support \"xray\" and \"hy\". If you need many cores, use \" \" to split\n  Xray"
  },
  {
    "path": "example/custom_inbound.json",
    "chars": 403,
    "preview": "[\n    {\n        \"listen\": \"0.0.0.0\",\n        \"port\": 1234,\n        \"protocol\": \"socks\",\n        \"settings\": {\n          "
  },
  {
    "path": "example/custom_outbound.json",
    "chars": 523,
    "preview": "[\n    {\n        \"tag\": \"IPv4_out\",\n        \"protocol\": \"freedom\",\n        \"settings\": {}\n    },\n    {\n        \"tag\": \"IP"
  },
  {
    "path": "example/dns.json",
    "chars": 110,
    "preview": "{\n    \"servers\": [\n        \"1.1.1.1\",\n        \"8.8.8.8\",\n        \"localhost\"\n    ],\n    \"tag\": \"dns_inbound\"\n}"
  },
  {
    "path": "example/route.json",
    "chars": 768,
    "preview": "{\n    \"domainStrategy\": \"IPOnDemand\",\n    \"rules\": [\n        {\n            \"type\": \"field\",\n            \"outboundTag\": \""
  },
  {
    "path": "example/rulelist",
    "chars": 47,
    "preview": "(.+\\.|^)(360|so)\\.(cn|com)\nbaidu.com\ngoogle.com"
  },
  {
    "path": "go.mod",
    "chars": 10628,
    "preview": "module github.com/Yuzuki616/V2bX\n\ngo 1.19\n\nrequire (\n\tgithub.com/Yuzuki616/hysteria/core v0.0.0-20230722103310-05508b7e5"
  },
  {
    "path": "go.sum",
    "chars": 110081,
    "preview": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.31.0/go.mod h1"
  },
  {
    "path": "limiter/clear.go",
    "chars": 337,
    "preview": "package limiter\n\nimport log \"github.com/sirupsen/logrus\"\n\nfunc ClearOnlineIP() error {\n\tlog.WithField(\"Type\", \"Limiter\")"
  },
  {
    "path": "limiter/conn.go",
    "chars": 3103,
    "preview": "package limiter\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\ntype ConnLimiter struct {\n\trealtime  bool\n\tipLimit   int\n\tconnLimit int\n\tco"
  },
  {
    "path": "limiter/conn_test.go",
    "chars": 1113,
    "preview": "package limiter\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar c *ConnLimiter\n\nfunc init() {\n\tc = NewConnLimiter(1, 1, true"
  },
  {
    "path": "limiter/dynamic.go",
    "chars": 859,
    "preview": "package limiter\n\nimport (\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/common/format\"\n\t\"time\"\n)\n\nf"
  },
  {
    "path": "limiter/limiter.go",
    "chars": 4216,
    "preview": "package limiter\n\nimport (\n\t\"errors\"\n\t\"regexp\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuk"
  },
  {
    "path": "limiter/rule.go",
    "chars": 723,
    "preview": "package limiter\n\nimport (\n\t\"regexp\"\n\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n)\n\nfunc (l *Limiter) CheckDomainRule(destina"
  },
  {
    "path": "main.go",
    "chars": 81,
    "preview": "package main\n\nimport \"github.com/Yuzuki616/V2bX/cmd\"\n\nfunc main() {\n\tcmd.Run()\n}\n"
  },
  {
    "path": "node/cert.go",
    "chars": 1197,
    "preview": "package node\n\nimport (\n\t\"fmt\"\n\t\"github.com/Yuzuki616/V2bX/common/file\"\n\t\"github.com/Yuzuki616/V2bX/node/lego\"\n\t\"log\"\n)\n\n"
  },
  {
    "path": "node/controller.go",
    "chars": 3194,
    "preview": "package node\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/Yuzuki616/V2bX/api/iprecoder\"\n\t\"github.com/Yuzuki616/V2bX/api/pane"
  },
  {
    "path": "node/lego/cert.go",
    "chars": 2564,
    "preview": "package lego\n\nimport (\n\t\"fmt\"\n\t\"github.com/go-acme/lego/v4/certcrypto\"\n\t\"github.com/go-acme/lego/v4/certificate\"\n\t\"githu"
  },
  {
    "path": "node/lego/lego.go",
    "chars": 1085,
    "preview": "package lego\n\nimport (\n\t\"fmt\"\n\t\"github.com/Yuzuki616/V2bX/common/file\"\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\t\"github.com/go"
  },
  {
    "path": "node/lego/lego_test.go",
    "chars": 625,
    "preview": "package lego\n\nimport (\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\t\"log\"\n\t\"os\"\n\t\"testing\"\n)\n\nvar l *Lego\n\nfunc init() {\n\tvar err "
  },
  {
    "path": "node/lego/user.go",
    "chars": 3094,
    "preview": "package lego\n\nimport (\n\t\"crypto\"\n\t\"crypto/ecdsa\"\n\t\"crypto/elliptic\"\n\t\"crypto/rand\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"fmt\""
  },
  {
    "path": "node/node.go",
    "chars": 914,
    "preview": "package node\n\nimport (\n\t\"fmt\"\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\tvCore \"github.co"
  },
  {
    "path": "node/task.go",
    "chars": 5673,
    "preview": "package node\n\nimport (\n\t\"time\"\n\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/common/task\"\n\tvCore \""
  },
  {
    "path": "node/user.go",
    "chars": 1722,
    "preview": "package node\n\nimport (\n\t\"runtime\"\n\t\"strconv\"\n\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\tlog \"github.com/sirupsen/logrus\"\n)"
  },
  {
    "path": "test_data/1.key",
    "chars": 1675,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAyryUpSI01T4jgPywpt4CIaf+MNgPn9DxHtJmuo1VB7Ysk13Z\nuOBVfS9HJoikbrcVeqENkR8"
  },
  {
    "path": "test_data/1.pem",
    "chars": 1399,
    "preview": "-----BEGIN CERTIFICATE-----\nMIID2zCCAsOgAwIBAgIRAJP0pRlp9k2eiBLK2a2B7LYwDQYJKoZIhvcNAQELBQAw\nXjELMAkGA1UEBhMCQ04xDjAMBgN"
  }
]

About this extraction

This page contains the full source code of the Yuzuki616/V2bX GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 110 files (315.0 KB), approximately 125.3k tokens, and a symbol index with 353 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!