[
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.md",
    "content": "---\nname: \"Bug 反馈\"\nabout: 创建一个报告以帮助我们修复并改进XrayR\ntitle: ''\nlabels: awaiting reply, bug\nassignees: ''\n---\n\n**描述该错误**\n简单地描述一下这个bug是什么\n\n**复现**\n复现该bug的步骤\n\n**环境和版本**\n - 系统 [例如：Debian 11]\n - 架构 [例如：AMD64]\n - 面板 [例如：V2board]\n - 协议 [例如：vmess]\n - 版本 [例如：0.8.2.2]\n - 部署方式 [例如：一键脚本]\n\n**日志和错误**\n请使用`xrayr log`查看并添加日志，以帮助解释你的问题\n\n**额外的内容**\n在这里添加关于问题的任何其他内容"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.md",
    "content": "---\nname: \"功能建议\"\nabout: 给XrayR提出建议，让我们做得更好\ntitle: ''\nlabels: awaiting reply, feature-request\nassignees: ''\n---\n\n**描述您想要的功能**\n\n清晰简洁的功能描述。\n\n**描述您考虑过的替代方案** \n\n是否有任何替代方案可以解决这个问题？ \n\n**附加上下文** \n\n在此处添加有关功能请求的任何其他上下文或截图。"
  },
  {
    "path": ".github/build/friendly-filenames.json",
    "content": "{\n    \"android-arm64\": { \"friendlyName\": \"android-arm64-v8a\" },\n    \"darwin-amd64\": { \"friendlyName\": \"macos-64\" },\n    \"darwin-arm64\": { \"friendlyName\": \"macos-arm64-v8a\" },\n    \"dragonfly-amd64\": { \"friendlyName\": \"dragonfly-64\" },\n    \"freebsd-386\": { \"friendlyName\": \"freebsd-32\" },\n    \"freebsd-amd64\": { \"friendlyName\": \"freebsd-64\" },\n    \"freebsd-arm64\": { \"friendlyName\": \"freebsd-arm64-v8a\" },\n    \"freebsd-arm7\": { \"friendlyName\": \"freebsd-arm32-v7a\" },\n    \"linux-386\": { \"friendlyName\": \"linux-32\" },\n    \"linux-amd64\": { \"friendlyName\": \"linux-64\" },\n    \"linux-arm5\": { \"friendlyName\": \"linux-arm32-v5\" },\n    \"linux-arm64\": { \"friendlyName\": \"linux-arm64-v8a\" },\n    \"linux-arm6\": { \"friendlyName\": \"linux-arm32-v6\" },\n    \"linux-arm7\": { \"friendlyName\": \"linux-arm32-v7a\" },\n    \"linux-mips64le\": { \"friendlyName\": \"linux-mips64le\" },\n    \"linux-mips64\": { \"friendlyName\": \"linux-mips64\" },\n    \"linux-mipslesoftfloat\": { \"friendlyName\": \"linux-mips32le-softfloat\" },\n    \"linux-mipsle\": { \"friendlyName\": \"linux-mips32le\" },\n    \"linux-mipssoftfloat\": { \"friendlyName\": \"linux-mips32-softfloat\" },\n    \"linux-mips\": { \"friendlyName\": \"linux-mips32\" },\n    \"linux-ppc64le\": { \"friendlyName\": \"linux-ppc64le\" },\n    \"linux-ppc64\": { \"friendlyName\": \"linux-ppc64\" },\n    \"linux-riscv64\": { \"friendlyName\": \"linux-riscv64\" },\n    \"linux-s390x\": { \"friendlyName\": \"linux-s390x\" },\n    \"openbsd-386\": { \"friendlyName\": \"openbsd-32\" },\n    \"openbsd-amd64\": { \"friendlyName\": \"openbsd-64\" },\n    \"openbsd-arm64\": { \"friendlyName\": \"openbsd-arm64-v8a\" },\n    \"openbsd-arm7\": { \"friendlyName\": \"openbsd-arm32-v7a\" },\n    \"windows-386\": { \"friendlyName\": \"windows-32\" },\n    \"windows-amd64\": { \"friendlyName\": \"windows-64\" },\n    \"windows-arm7\": { \"friendlyName\": \"windows-arm32-v7a\" }\n  }"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"gomod\" # See documentation for possible values\n    directory: \"/\" # Location of package manifests\n    schedule:\n      interval: \"daily\"\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [ master ]\n  schedule:\n    - cron: '43 22 * * 3'\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'go' ]\n        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]\n        # Learn more:\n        # 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\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v2\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v1\n      with:\n        languages: ${{ matrix.language }}\n        # If you wish to specify custom queries, you can do so here or in a config file.\n        # By default, queries listed here will override any specified in a config file.\n        # Prefix the list here with \"+\" to use these queries and those in the config file.\n        # queries: ./path/to/local/query, your-org/your-repo/queries@main\n\n    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n    # If this step fails, then you should remove it and run the build manually (see below)\n    - name: Autobuild\n      uses: github/codeql-action/autobuild@v1\n\n    # ℹ️ Command-line programs to run using the OS shell.\n    # 📚 https://git.io/JvXDl\n\n    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines\n    #    and modify them (or add more) to build your code if your project\n    #    uses a compiled language\n\n    #- run: |\n    #   make bootstrap\n    #   make release\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v1\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Build and Release\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - master\n    paths:\n      - \"**/*.go\"\n      - \"go.mod\"\n      - \"go.sum\"\n      - \".github/workflows/*.yml\"\n  pull_request:\n    types: [opened, synchronize, reopened]\n    paths:\n      - \"**/*.go\"\n      - \"go.mod\"\n      - \"go.sum\"\n      - \".github/workflows/*.yml\"\n  release:\n    types: [published]\n\njobs:\n\n  build:\n    strategy:\n      matrix:\n        # Include amd64 on all platforms.\n        goos: [windows, freebsd, linux, dragonfly, darwin]\n        goarch: [amd64, 386]\n        exclude:\n          # Exclude i386 on darwin and dragonfly.\n          - goarch: 386\n            goos: dragonfly\n          - goarch: 386\n            goos: darwin\n        include:\n          # BEIGIN MacOS ARM64\n          - goos: darwin\n            goarch: arm64\n          # END MacOS ARM64\n          # BEGIN Linux ARM 5 6 7\n          - goos: linux\n            goarch: arm\n            goarm: 7\n          - goos: linux\n            goarch: arm\n            goarm: 6\n          - goos: linux\n            goarch: arm\n            goarm: 5\n          # END Linux ARM 5 6 7\n          # BEGIN Android ARM 8\n          - goos: android\n            goarch: arm64\n          # END Android ARM 8\n          # BEGIN Other architectures\n          # BEGIN riscv64 & ARM64\n          - goos: linux\n            goarch: arm64\n          - goos: linux\n            goarch: riscv64\n          # END riscv64 & ARM64\n          # BEGIN MIPS\n          - goos: linux\n            goarch: mips64\n          - goos: linux\n            goarch: mips64le\n          - goos: linux\n            goarch: mipsle\n          - goos: linux\n            goarch: mips\n          # END MIPS\n          # BEGIN PPC\n          - goos: linux\n            goarch: ppc64\n          - goos: linux\n            goarch: ppc64le\n          # END PPC\n          # BEGIN FreeBSD ARM\n          - goos: freebsd\n            goarch: arm64\n          - goos: freebsd\n            goarch: arm\n            goarm: 7\n          # END FreeBSD ARM\n          # BEGIN S390X\n          - goos: linux\n            goarch: s390x\n          # END S390X\n          # END Other architectures\n      fail-fast: false\n\n    runs-on: ubuntu-latest\n    env:\n      GOOS: ${{ matrix.goos }}\n      GOARCH: ${{ matrix.goarch }}\n      GOARM: ${{ matrix.goarm }}\n      CGO_ENABLED: 0\n    steps:\n      - name: Checkout codebase\n        uses: actions/checkout@v3\n      - name: Show workflow information\n        id: get_filename\n        run: |\n          export _NAME=$(jq \".[\\\"$GOOS-$GOARCH$GOARM$GOMIPS\\\"].friendlyName\" -r < .github/build/friendly-filenames.json)\n          echo \"GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME\"\n          echo \"::set-output name=ASSET_NAME::$_NAME\"\n          echo \"ASSET_NAME=$_NAME\" >> $GITHUB_ENV\n      - name: Set up Go\n        uses: actions/setup-go@v3\n        with:\n          go-version: '1.19'\n\n      - name: Get project dependencies\n        run: go mod download\n      - name: Get release version\n        if: ${{ github.event_name == 'release' }}\n        run: |\n          echo \"version=$(echo $GITHUB_REF | cut -d / -f 3)\" >> $GITHUB_ENV\n      - name: Get other version\n        if: ${{ github.event_name != 'release' }}\n        run: |\n          echo \"version=${{ github.sha }}\" >> $GITHUB_ENV\n      - name: Build V2bX\n        run: |\n          echo \"version: $version\"\n          mkdir -p build_assets\n          go build -v -o build_assets/V2bX -tags \"xray hy\" -trimpath -ldflags \"-X 'github.com/Yuzuki616/V2bX/cmd.version=$version' -s -w -buildid=\"\n\n      - name: Build Mips softfloat V2bX\n        if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'\n        run: |\n          echo \"version: $version\"\n          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=\"\n      - name: Rename Windows V2bX\n        if: matrix.goos == 'windows'\n        run: |\n          cd ./build_assets || exit 1\n          mv V2bX V2bX.exe\n      - name: Prepare to release\n        run: |\n          cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md\n          cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE\n          cp ${GITHUB_WORKSPACE}/example/dns.json ./build_assets/dns.json\n          cp ${GITHUB_WORKSPACE}/example/route.json ./build_assets/route.json\n          cp ${GITHUB_WORKSPACE}/example/custom_outbound.json ./build_assets/custom_outbound.json\n          cp ${GITHUB_WORKSPACE}/example/custom_inbound.json ./build_assets/custom_inbound.json\n          cp ${GITHUB_WORKSPACE}/example/rulelist ./build_assets/rulelist\n          cp ${GITHUB_WORKSPACE}/example/config.yml.example ./build_assets/config.yml\n          LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')\n          for i in \"${LIST[@]}\"\n          do\n            INFO=($(echo $i | awk 'BEGIN{FS=\" \";OFS=\" \"} {print $1,$2,$3}'))\n            FILE_NAME=\"${INFO[2]}.dat\"\n            echo -e \"Downloading ${FILE_NAME}...\"\n            curl -L \"https://github.com/v2fly/${INFO[0]}/releases/latest/download/${INFO[1]}.dat\" -o ./build_assets/${FILE_NAME}\n            echo -e \"Verifying HASH key...\"\n            HASH=\"$(curl -sL \"https://github.com/v2fly/${INFO[0]}/releases/latest/download/${INFO[1]}.dat.sha256sum\" | awk -F ' ' '{print $1}')\"\n            [ \"$(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; }\n          done\n      - name: Create ZIP archive\n        shell: bash\n        run: |\n          pushd build_assets || exit 1\n          touch -mt $(date +%Y01010000) *\n          zip -9vr ../V2bX-$ASSET_NAME.zip .\n          popd || exit 1\n          FILE=./V2bX-$ASSET_NAME.zip\n          DGST=$FILE.dgst\n          for METHOD in {\"md5\",\"sha1\",\"sha256\",\"sha512\"}\n          do\n            openssl dgst -$METHOD $FILE | sed 's/([^)]*)//g' >>$DGST\n          done\n      - name: Change the name\n        run: |\n          mv build_assets V2bX-$ASSET_NAME\n      - name: Upload files to Artifacts\n        uses: actions/upload-artifact@v3\n        with:\n          name: V2bX-${{ steps.get_filename.outputs.ASSET_NAME }}\n          path: |\n            ./V2bX-${{ steps.get_filename.outputs.ASSET_NAME }}/*\n      - name: Upload binaries to release\n        uses: svenstaro/upload-release-action@v2\n        if: github.event_name == 'release'\n        with:\n          repo_token: ${{ secrets.GITHUB_TOKEN }}\n          file: ./V2bX-${{ steps.get_filename.outputs.ASSET_NAME }}.zip*\n          tag: ${{ github.ref }}\n          file_glob: true\n"
  },
  {
    "path": ".gitignore",
    "content": "example/config.yml\nexample/main\nexample/XrayR\nexample/XrayR*\nexample/mytest\nexample/access.logo\nexample/error.log\napi/chooseparser.go.bak\napp/Inboundbuilder/.lego/\napp/legocmd/.lego/\n.vscode/launch.json\nexample/.lego\nexample/cert\n./vscode\n.idea/*"
  },
  {
    "path": "LICENSE",
    "content": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n    means each individual or legal entity that creates, contributes to\n    the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n    means the combination of the Contributions of others (if any) used\n    by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n    means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n    means Source Code Form to which the initial Contributor has attached\n    the notice in Exhibit A, the Executable Form of such Source Code\n    Form, and Modifications of such Source Code Form, in each case\n    including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n    means\n\n    (a) that the initial Contributor has attached the notice described\n        in Exhibit B to the Covered Software; or\n\n    (b) that the Covered Software was made available under the terms of\n        version 1.1 or earlier of the License, but not also under the\n        terms of a Secondary License.\n\n1.6. \"Executable Form\"\n    means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n    means a work that combines Covered Software with other material, in\n    a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n    means this document.\n\n1.9. \"Licensable\"\n    means having the right to grant, to the maximum extent possible,\n    whether at the time of the initial grant or subsequently, any and\n    all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n    means any of the following:\n\n    (a) any file in Source Code Form that results from an addition to,\n        deletion from, or modification of the contents of Covered\n        Software; or\n\n    (b) any new file in Source Code Form that contains any Covered\n        Software.\n\n1.11. \"Patent Claims\" of a Contributor\n    means any patent claim(s), including without limitation, method,\n    process, and apparatus claims, in any patent Licensable by such\n    Contributor that would be infringed, but for the grant of the\n    License, by the making, using, selling, offering for sale, having\n    made, import, or transfer of either its Contributions or its\n    Contributor Version.\n\n1.12. \"Secondary License\"\n    means either the GNU General Public License, Version 2.0, the GNU\n    Lesser General Public License, Version 2.1, the GNU Affero General\n    Public License, Version 3.0, or any later versions of those\n    licenses.\n\n1.13. \"Source Code Form\"\n    means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n    means an individual or a legal entity exercising rights under this\n    License. For legal entities, \"You\" includes any entity that\n    controls, is controlled by, or is under common control with You. For\n    purposes of this definition, \"control\" means (a) the power, direct\n    or indirect, to cause the direction or management of such entity,\n    whether by contract or otherwise, or (b) ownership of more than\n    fifty percent (50%) of the outstanding shares or beneficial\n    ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n    Licensable by such Contributor to use, reproduce, make available,\n    modify, display, perform, distribute, and otherwise exploit its\n    Contributions, either on an unmodified basis, with Modifications, or\n    as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n    for sale, have made, import, and otherwise transfer either its\n    Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n    or\n\n(b) for infringements caused by: (i) Your and any other third party's\n    modifications of Covered Software, or (ii) the combination of its\n    Contributions with other software (except as part of its Contributor\n    Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n    its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n    Form, as described in Section 3.1, and You must inform recipients of\n    the Executable Form how they can obtain a copy of such Source Code\n    Form by reasonable means in a timely manner, at a charge no more\n    than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n    License, or sublicense it under different terms, provided that the\n    license for the Executable Form does not attempt to limit or alter\n    the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n*                                                                      *\n*  6. Disclaimer of Warranty                                           *\n*  -------------------------                                           *\n*                                                                      *\n*  Covered Software is provided under this License on an \"as is\"       *\n*  basis, without warranty of any kind, either expressed, implied, or  *\n*  statutory, including, without limitation, warranties that the       *\n*  Covered Software is free of defects, merchantable, fit for a        *\n*  particular purpose or non-infringing. The entire risk as to the     *\n*  quality and performance of the Covered Software is with You.        *\n*  Should any Covered Software prove defective in any respect, You     *\n*  (not any Contributor) assume the cost of any necessary servicing,   *\n*  repair, or correction. This disclaimer of warranty constitutes an   *\n*  essential part of this License. No use of any Covered Software is   *\n*  authorized under this License except under this disclaimer.         *\n*                                                                      *\n************************************************************************\n\n************************************************************************\n*                                                                      *\n*  7. Limitation of Liability                                          *\n*  --------------------------                                          *\n*                                                                      *\n*  Under no circumstances and under no legal theory, whether tort      *\n*  (including negligence), contract, or otherwise, shall any           *\n*  Contributor, or anyone who distributes Covered Software as          *\n*  permitted above, be liable to You for any direct, indirect,         *\n*  special, incidental, or consequential damages of any character      *\n*  including, without limitation, damages for lost profits, loss of    *\n*  goodwill, work stoppage, computer failure or malfunction, or any    *\n*  and all other commercial damages or losses, even if such party      *\n*  shall have been informed of the possibility of such damages. This   *\n*  limitation of liability shall not apply to liability for death or   *\n*  personal injury resulting from such party's negligence to the       *\n*  extent applicable law prohibits such limitation. Some               *\n*  jurisdictions do not allow the exclusion or limitation of           *\n*  incidental or consequential damages, so this exclusion and          *\n*  limitation may not apply to You.                                    *\n*                                                                      *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n  This Source Code Form is subject to the terms of the Mozilla Public\n  License, v. 2.0. If a copy of the MPL was not distributed with this\n  file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n  This Source Code Form is \"Incompatible With Secondary Licenses\", as\n  defined by the Mozilla Public License, v. 2.0.\n"
  },
  {
    "path": "README.md",
    "content": "# V2bX\n**项目转移至[V2bX](https://github.com/InazumaV/V2bX)，此仓库不再维护**\n\n[![](https://img.shields.io/badge/TgChat-%E4%BA%A4%E6%B5%81%E7%BE%A4-blue)](https://t.me/YuzukiProjects)\n\nA V2board node server based on multi core, modified from XrayR.  \n一个基于多种内核的V2board节点服务端，修改自XrayR，支持V2ay,Trojan,Shadowsocks协议。\n\n**注意：1.1.0将更换为V2board1.7.0之后新增的Api，原Api将被移除，请1.7.0之前的用户使用1.1.0之前的版本。**\n\n## 特点\n\n* 永久开源且免费。\n* 支持V2ray，Trojan， Shadowsocks多种协议。\n* 支持Vless和XTLS等新特性。\n* 支持单实例对接多节点，无需重复启动。\n* 支持限制在线IP。\n* 支持限制Tcp连接数。\n* 支持节点端口级别、用户级别限速。\n* 配置简单明了。\n* 修改配置自动重启实例。\n* 支持多种内核，易扩展。\n* 支持条件编译，可仅编译需要的内核。\n\n## 功能介绍\n\n| 功能        | v2ray | trojan | shadowsocks | hysteria |\n|-----------|-------|--------|-------------|----------|\n| 自动申请tls证书 | √     | √      | √           | √        |\n| 自动续签tls证书 | √     | √      | √           | √        |\n| 在线人数统计    | √     | √      | √           | √        |\n| 审计规则      | √     | √      | √           |          |\n| 自定义DNS    | √     | √      | √           | √        |\n| 在线IP数限制   | √     | √      | √           | √        |\n| 连接数限制     | √     | √      | √           |          |\n| 跨节点IP数限制  | √     | √      | √           |          |\n| 按照用户限速    | √     | √      | √           |          |\n| 动态限速(未测试) | √     | √      | √           |          |\n\n## TODO\n\n- [ ] 重新实现动态限速\n- [ ] 重新实现在线IP同步（跨节点在线IP限制）\n- [x] 集成基本操作Command(Start, Stop, Restart, Status, Uninstall)\n- [ ] 完善Hysteria内核支持\n- [ ] 完善使用文档\n- [ ] 尽可能统一日志输出格式\n\n## 软件安装\n\n### 一键安装\n\n```\nwget -N https://raw.githubusercontents.com/Yuzuki616/V2bX-script/master/install.sh && bash install.sh\n```\n\n### 手动安装\n\n[手动安装教程（过时待更新）](https://yuzuki-1.gitbook.io/v2bx-doc/xrayr-xia-zai-he-an-zhuang/install/manual)\n\n## 构建\n``` bash\n# 通过-tag选项指定要编译的内核， 可选 xray， hy\ngo build -o V2bX -ldflags '-s -w' -gcflags=\"all=-trimpath=${PWD}\" -asmflags=\"all=-trimpath=${PWD} -tags \"xray hy\"\n```\n\n## 配置文件及详细使用教程\n\n[详细使用教程](https://yuzuki-1.gitbook.io/v2bx-doc/)\n\n## 免责声明\n\n* 此项目用于本人自用，因此本人不能保证向后兼容性。\n* 由于本人能力有限，不能保证所有功能的可用性，如果出现问题请在Issues反馈。\n* 本人不对任何人使用本项目造成的任何后果承担责任。\n* 本人比较多变，因此本项目可能会随想法或思路的变动随性更改项目结构或大规模重构代码，若不能接受请勿使用。\n\n## Thanks\n\n* [Project X](https://github.com/XTLS/)\n* [V2Fly](https://github.com/v2fly)\n* [VNet-V2ray](https://github.com/ProxyPanel/VNet-V2ray)\n* [Air-Universe](https://github.com/crossfw/Air-Universe)\n* [XrayR](https://github.com/XrayR/XrayR)\n\n## Stars 增长记录\n\n[![Stargazers over time](https://starchart.cc/Yuzuki616/V2bX.svg)](https://starchart.cc/Yuzuki616/V2bX)\n"
  },
  {
    "path": "api/iprecoder/interface.go",
    "content": "package iprecoder\n\nimport (\n\t\"github.com/Yuzuki616/V2bX/limiter\"\n)\n\ntype IpRecorder interface {\n\tSyncOnlineIp(Ips []limiter.UserIpList) ([]limiter.UserIpList, error)\n}\n"
  },
  {
    "path": "api/iprecoder/recorder.go",
    "content": "package iprecoder\n\nimport (\n\t\"errors\"\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\t\"github.com/Yuzuki616/V2bX/limiter\"\n\t\"github.com/go-resty/resty/v2\"\n\t\"github.com/goccy/go-json\"\n\t\"time\"\n)\n\ntype Recorder struct {\n\tclient *resty.Client\n\t*conf.RecorderConfig\n}\n\nfunc NewRecorder(c *conf.RecorderConfig) *Recorder {\n\treturn &Recorder{\n\t\tclient:         resty.New().SetTimeout(time.Duration(c.Timeout) * time.Second),\n\t\tRecorderConfig: c,\n\t}\n}\n\nfunc (r *Recorder) SyncOnlineIp(ips []limiter.UserIpList) ([]limiter.UserIpList, error) {\n\trsp, err := r.client.R().\n\t\tSetBody(ips).\n\t\tPost(r.Url + \"/api/v1/SyncOnlineIp?token=\" + r.Token)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif rsp.StatusCode() != 200 {\n\t\treturn nil, errors.New(rsp.String())\n\t}\n\tips = []limiter.UserIpList{}\n\terr = json.Unmarshal(rsp.Body(), &ips)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn ips, nil\n}\n"
  },
  {
    "path": "api/iprecoder/redis.go",
    "content": "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\"github.com/go-redis/redis/v8\"\n\t\"strconv\"\n\t\"time\"\n)\n\ntype Redis struct {\n\t*conf.RedisConfig\n\tclient *redis.Client\n}\n\nfunc NewRedis(c *conf.RedisConfig) *Redis {\n\treturn &Redis{\n\t\tRedisConfig: c,\n\t\tclient: redis.NewClient(&redis.Options{\n\t\t\tAddr:     c.Address,\n\t\t\tPassword: c.Password,\n\t\t\tDB:       c.Db,\n\t\t}),\n\t}\n}\n\nfunc (r *Redis) SyncOnlineIp(Ips []limiter.UserIpList) ([]limiter.UserIpList, error) {\n\tctx := context.Background()\n\tfor i := range Ips {\n\t\terr := r.client.SAdd(ctx, \"UserList\", Ips[i].Uid).Err()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"add user failed: %s\", err)\n\t\t}\n\t\tr.client.Expire(ctx, \"UserList\", time.Second*time.Duration(r.Expiry))\n\t\tfor _, ip := range Ips[i].IpList {\n\t\t\terr := r.client.SAdd(ctx, strconv.Itoa(Ips[i].Uid), ip).Err()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"add ip failed: %s\", err)\n\t\t\t}\n\t\t\tr.client.Expire(ctx, strconv.Itoa(Ips[i].Uid), time.Second*time.Duration(r.Expiry))\n\t\t}\n\t}\n\tc := r.client.SMembers(ctx, \"UserList\")\n\tif c.Err() != nil {\n\t\treturn nil, fmt.Errorf(\"get user list failed: %s\", c.Err())\n\t}\n\tIps = make([]limiter.UserIpList, 0, len(c.Val()))\n\tfor _, uid := range c.Val() {\n\t\tuidInt, err := strconv.Atoi(uid)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"convert uid failed: %s\", err)\n\t\t}\n\t\tips := r.client.SMembers(ctx, uid)\n\t\tif ips.Err() != nil {\n\t\t\treturn nil, fmt.Errorf(\"get ip list failed: %s\", ips.Err())\n\t\t}\n\t\tIps = append(Ips, limiter.UserIpList{\n\t\t\tUid:    uidInt,\n\t\t\tIpList: ips.Val(),\n\t\t})\n\t}\n\treturn Ips, nil\n}\n"
  },
  {
    "path": "api/iprecoder/redis_test.go",
    "content": "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\nfunc TestRedis_SyncOnlineIp(t *testing.T) {\n\tr := NewRedis(&conf.RedisConfig{\n\t\tAddress:  \"127.0.0.1:6379\",\n\t\tPassword: \"\",\n\t\tDb:       0,\n\t})\n\tusers, err := r.SyncOnlineIp([]limiter.UserIpList{\n\t\t{1, []string{\"5.5.5.5\", \"4.4.4.4\"}},\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tlog.Println(users)\n}\n"
  },
  {
    "path": "api/panel/node.go",
    "content": "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/Yuzuki616/V2bX/common/crypt\"\n\t\"github.com/goccy/go-json\"\n\tlog \"github.com/sirupsen/logrus\"\n\tcoreConf \"github.com/xtls/xray-core/infra/conf\"\n)\n\ntype CommonNodeRsp struct {\n\tHost       string     `json:\"host\"`\n\tServerPort int        `json:\"server_port\"`\n\tServerName string     `json:\"server_name\"`\n\tRoutes     []Route    `json:\"routes\"`\n\tBaseConfig BaseConfig `json:\"base_config\"`\n}\n\ntype Route struct {\n\tId     int         `json:\"id\"`\n\tMatch  interface{} `json:\"match\"`\n\tAction string      `json:\"action\"`\n\t//ActionValue interface{} `json:\"action_value\"`\n}\ntype BaseConfig struct {\n\tPushInterval any `json:\"push_interval\"`\n\tPullInterval any `json:\"pull_interval\"`\n}\n\ntype V2rayNodeRsp struct {\n\tTls             int             `json:\"tls\"`\n\tNetwork         string          `json:\"network\"`\n\tNetworkSettings json.RawMessage `json:\"networkSettings\"`\n\tServerName      string          `json:\"server_name\"`\n}\n\ntype ShadowsocksNodeRsp struct {\n\tCipher    string `json:\"cipher\"`\n\tServerKey string `json:\"server_key\"`\n}\n\ntype HysteriaNodeRsp struct {\n\tUpMbps   int    `json:\"up_mbps\"`\n\tDownMbps int    `json:\"down_mbps\"`\n\tObfs     string `json:\"obfs\"`\n}\n\ntype NodeInfo struct {\n\tId              int\n\tType            string\n\tRules           Rules\n\tHost            string\n\tPort            int\n\tNetwork         string\n\tExtraConfig     V2rayExtraConfig\n\tNetworkSettings json.RawMessage\n\tTls             bool\n\tServerName      string\n\tUpMbps          int\n\tDownMbps        int\n\tServerKey       string\n\tCipher          string\n\tHyObfs          string\n\tPushInterval    time.Duration\n\tPullInterval    time.Duration\n}\n\ntype Rules struct {\n\tRegexp   []string\n\tProtocol []string\n}\n\ntype V2rayExtraConfig struct {\n\tEnableVless   string         `json:\"EnableVless\"`\n\tVlessFlow     string         `json:\"VlessFlow\"`\n\tEnableReality string         `json:\"EnableReality\"`\n\tRealityConfig *RealityConfig `json:\"RealityConfig\"`\n}\n\ntype RealityConfig struct {\n\tDest         interface{} `yaml:\"Dest\" json:\"Dest\"`\n\tXver         string      `yaml:\"Xver\" json:\"Xver\"`\n\tServerNames  []string    `yaml:\"ServerNames\" json:\"ServerNames\"`\n\tPrivateKey   string      `yaml:\"PrivateKey\" json:\"PrivateKey\"`\n\tMinClientVer string      `yaml:\"MinClientVer\" json:\"MinClientVer\"`\n\tMaxClientVer string      `yaml:\"MaxClientVer\" json:\"MaxClientVer\"`\n\tMaxTimeDiff  string      `yaml:\"MaxTimeDiff\" json:\"MaxTimeDiff\"`\n\tShortIds     []string    `yaml:\"ShortIds\" json:\"ShortIds\"`\n}\n\nfunc (c *Client) GetNodeInfo() (node *NodeInfo, err error) {\n\tconst path = \"/api/v1/server/UniProxy/config\"\n\tr, err := c.client.\n\t\tR().\n\t\tSetHeader(\"If-None-Match\", c.nodeEtag).\n\t\tGet(path)\n\tif err = c.checkResponse(r, path, err); err != nil {\n\t\treturn\n\t}\n\tif r.StatusCode() == 304 {\n\t\treturn nil, nil\n\t}\n\t// parse common params\n\tnode = &NodeInfo{\n\t\tId:   c.NodeId,\n\t\tType: c.NodeType,\n\t}\n\tcommon := CommonNodeRsp{}\n\terr = json.Unmarshal(r.Body(), &common)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"decode common params error: %s\", err)\n\t}\n\tfor i := range common.Routes { // parse rules from routes\n\t\tvar matchs []string\n\t\tif _, ok := common.Routes[i].Match.(string); ok {\n\t\t\tmatchs = strings.Split(common.Routes[i].Match.(string), \",\")\n\t\t} else if _, ok = common.Routes[i].Match.([]string); ok {\n\t\t\tmatchs = common.Routes[i].Match.([]string)\n\t\t} else {\n\t\t\ttemp := common.Routes[i].Match.([]interface{})\n\t\t\tmatchs = make([]string, len(temp))\n\t\t\tfor i := range temp {\n\t\t\t\tmatchs[i] = temp[i].(string)\n\t\t\t}\n\t\t}\n\t\tswitch common.Routes[i].Action {\n\t\tcase \"block\":\n\t\t\tfor _, v := range matchs {\n\t\t\t\tif strings.HasPrefix(v, \"protocol:\") {\n\t\t\t\t\t// protocol\n\t\t\t\t\tnode.Rules.Protocol = append(node.Rules.Protocol, strings.TrimPrefix(v, \"protocol:\"))\n\t\t\t\t} else {\n\t\t\t\t\t// domain\n\t\t\t\t\tnode.Rules.Regexp = append(node.Rules.Regexp, strings.TrimPrefix(v, \"regexp:\"))\n\t\t\t\t}\n\t\t\t}\n\t\tcase \"dns\":\n\t\t\tif matchs[0] != \"main\" {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tdnsPath := os.Getenv(\"XRAY_DNS_PATH\")\n\t\t\tif dnsPath == \"\" {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tdns := []byte(strings.Join(matchs[1:], \"\"))\n\t\t\tcurrentData, err := os.ReadFile(dnsPath)\n\t\t\tif err != nil {\n\t\t\t\tlog.WithField(\"err\", err).Panic(\"Failed to read XRAY_DNS_PATH\")\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif !bytes.Equal(currentData, dns) {\n\t\t\t\tcoreDnsConfig := &coreConf.DNSConfig{}\n\t\t\t\tif err = json.NewDecoder(bytes.NewReader(dns)).Decode(coreDnsConfig); err != nil {\n\t\t\t\t\tlog.WithField(\"err\", err).Panic(\"Failed to unmarshal DNS config\")\n\t\t\t\t}\n\t\t\t\t_, err := coreDnsConfig.Build()\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.WithField(\"err\", err).Panic(\"Failed to understand DNS config, Please check: https://xtls.github.io/config/dns.html for help\")\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif err = os.Truncate(dnsPath, 0); err != nil {\n\t\t\t\t\tlog.WithField(\"err\", err).Panic(\"Failed to clear XRAY DNS PATH file\")\n\t\t\t\t}\n\t\t\t\tif err = os.WriteFile(dnsPath, dns, 0644); err != nil {\n\t\t\t\t\tlog.WithField(\"err\", err).Panic(\"Failed to write DNS to XRAY DNS PATH file\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tnode.ServerName = common.ServerName\n\tnode.Host = common.Host\n\tnode.Port = common.ServerPort\n\tnode.PullInterval = intervalToTime(common.BaseConfig.PullInterval)\n\tnode.PushInterval = intervalToTime(common.BaseConfig.PushInterval)\n\t// parse protocol params\n\tswitch c.NodeType {\n\tcase \"v2ray\":\n\t\trsp := V2rayNodeRsp{}\n\t\terr = json.Unmarshal(r.Body(), &rsp)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"decode v2ray params error: %s\", err)\n\t\t}\n\t\tnode.Network = rsp.Network\n\t\tnode.NetworkSettings = rsp.NetworkSettings\n\t\tnode.ServerName = rsp.ServerName\n\t\tif rsp.Tls == 1 {\n\t\t\tnode.Tls = true\n\t\t}\n\t\terr = json.Unmarshal(rsp.NetworkSettings, &node.ExtraConfig)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"decode v2ray extra error: %s\", err)\n\t\t}\n\t\tif node.ExtraConfig.EnableReality == \"true\" {\n\t\t\tif node.ExtraConfig.RealityConfig == nil {\n\t\t\t\tnode.ExtraConfig.EnableReality = \"false\"\n\t\t\t} else {\n\t\t\t\tkey := crypt.GenX25519Private([]byte(strconv.Itoa(c.NodeId) + c.NodeType + c.Token +\n\t\t\t\t\tnode.ExtraConfig.RealityConfig.PrivateKey))\n\t\t\t\tnode.ExtraConfig.RealityConfig.PrivateKey = base64.RawURLEncoding.EncodeToString(key)\n\t\t\t}\n\t\t}\n\tcase \"shadowsocks\":\n\t\trsp := ShadowsocksNodeRsp{}\n\t\terr = json.Unmarshal(r.Body(), &rsp)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"decode v2ray params error: %s\", err)\n\t\t}\n\t\tnode.ServerKey = rsp.ServerKey\n\t\tnode.Cipher = rsp.Cipher\n\tcase \"trojan\":\n\t\tnode.Tls = true\n\tcase \"hysteria\":\n\t\trsp := HysteriaNodeRsp{}\n\t\terr = json.Unmarshal(r.Body(), &rsp)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"decode v2ray params error: %s\", err)\n\t\t}\n\t\tnode.DownMbps = rsp.DownMbps\n\t\tnode.UpMbps = rsp.UpMbps\n\t\tnode.HyObfs = rsp.Obfs\n\t}\n\tc.nodeEtag = r.Header().Get(\"ETag\")\n\treturn\n}\n\nfunc intervalToTime(i interface{}) time.Duration {\n\tswitch reflect.TypeOf(i).Kind() {\n\tcase reflect.Int:\n\t\treturn time.Duration(i.(int)) * time.Second\n\tcase reflect.String:\n\t\ti, _ := strconv.Atoi(i.(string))\n\t\treturn time.Duration(i) * time.Second\n\tcase reflect.Float64:\n\t\treturn time.Duration(i.(float64)) * time.Second\n\tdefault:\n\t\treturn time.Duration(reflect.ValueOf(i).Int()) * time.Second\n\t}\n}\n"
  },
  {
    "path": "api/panel/node_test.go",
    "content": "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 := New(&conf.ApiConfig{\n\t\tAPIHost:  \"http://127.0.0.1\",\n\t\tKey:      \"token\",\n\t\tNodeType: \"V2ray\",\n\t\tNodeID:   1,\n\t})\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tclient = c\n}\n\nfunc TestClient_GetNodeInfo(t *testing.T) {\n\tlog.Println(client.GetNodeInfo())\n\tlog.Println(client.GetNodeInfo())\n}\n\nfunc TestClient_ReportUserTraffic(t *testing.T) {\n\tlog.Println(client.ReportUserTraffic([]UserTraffic{\n\t\t{\n\t\t\tUID:      10372,\n\t\t\tUpload:   1000,\n\t\t\tDownload: 1000,\n\t\t},\n\t}))\n}\n"
  },
  {
    "path": "api/panel/panel.go",
    "content": "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/V2bX/conf\"\n\t\"github.com/go-resty/resty/v2\"\n)\n\n// Panel is the interface for different panel's api.\n\ntype Client struct {\n\tclient        *resty.Client\n\tAPIHost       string\n\tToken         string\n\tNodeType      string\n\tNodeId        int\n\tLocalRuleList []*regexp.Regexp\n\tnodeEtag      string\n\tuserEtag      string\n}\n\nfunc New(c *conf.ApiConfig) (*Client, error) {\n\tclient := resty.New()\n\tclient.SetRetryCount(3)\n\tif c.Timeout > 0 {\n\t\tclient.SetTimeout(time.Duration(c.Timeout) * time.Second)\n\t} else {\n\t\tclient.SetTimeout(5 * time.Second)\n\t}\n\tclient.OnError(func(req *resty.Request, err error) {\n\t\tif v, ok := err.(*resty.ResponseError); ok {\n\t\t\t// v.Response contains the last response from the server\n\t\t\t// v.Err contains the original error\n\t\t\tlog.Print(v.Err)\n\t\t}\n\t})\n\tclient.SetBaseURL(c.APIHost)\n\t// Check node type\n\tc.NodeType = strings.ToLower(c.NodeType)\n\tswitch c.NodeType {\n\tcase \"v2ray\", \"trojan\", \"shadowsocks\", \"hysteria\":\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupported Node type: %s\", c.NodeType)\n\t}\n\t// set params\n\tclient.SetQueryParams(map[string]string{\n\t\t\"node_type\": c.NodeType,\n\t\t\"node_id\":   strconv.Itoa(c.NodeID),\n\t\t\"token\":     c.Key,\n\t})\n\t// Read local rule list\n\tlocalRuleList := readLocalRuleList(c.RuleListPath)\n\treturn &Client{\n\t\tclient:        client,\n\t\tToken:         c.Key,\n\t\tAPIHost:       c.APIHost,\n\t\tNodeType:      c.NodeType,\n\t\tNodeId:        c.NodeID,\n\t\tLocalRuleList: localRuleList,\n\t}, nil\n}\n\n// readLocalRuleList reads the local rule list file\nfunc readLocalRuleList(path string) (LocalRuleList []*regexp.Regexp) {\n\tLocalRuleList = make([]*regexp.Regexp, 0)\n\tif path != \"\" {\n\t\t// open the file\n\t\tfile, err := os.Open(path)\n\t\t//handle errors while opening\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error when opening file: %s\", err)\n\t\t\treturn\n\t\t}\n\t\tfileScanner := bufio.NewScanner(file)\n\t\t// read line by line\n\t\tfor fileScanner.Scan() {\n\t\t\tLocalRuleList = append(LocalRuleList, regexp.MustCompile(fileScanner.Text()))\n\t\t}\n\t\t// handle first encountered error while reading\n\t\tif err := fileScanner.Err(); err != nil {\n\t\t\tlog.Fatalf(\"Error while reading file: %s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "api/panel/user.go",
    "content": "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 UserInfo struct {\n\tId         int    `json:\"id\"`\n\tUuid       string `json:\"uuid\"`\n\tSpeedLimit int    `json:\"speed_limit\"`\n}\n\ntype UserListBody struct {\n\t//Msg  string `json:\"msg\"`\n\tUsers []UserInfo `json:\"users\"`\n}\n\n// GetUserList will pull user form sspanel\nfunc (c *Client) GetUserList() (UserList []UserInfo, err error) {\n\tconst path = \"/api/v1/server/UniProxy/user\"\n\tr, err := c.client.R().\n\t\tSetHeader(\"If-None-Match\", c.userEtag).\n\t\tGet(path)\n\terr = c.checkResponse(r, path, err)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\terr = c.checkResponse(r, path, err)\n\tif r.StatusCode() == 304 {\n\t\treturn nil, nil\n\t}\n\tvar userList *UserListBody\n\terr = json.Unmarshal(r.Body(), &userList)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshal userlist error: %s\", err)\n\t}\n\tc.userEtag = r.Header().Get(\"ETag\")\n\treturn userList.Users, nil\n}\n\ntype UserTraffic struct {\n\tUID      int\n\tUpload   int64\n\tDownload int64\n}\n\n// ReportUserTraffic reports the user traffic\nfunc (c *Client) ReportUserTraffic(userTraffic []UserTraffic) error {\n\tdata := make(map[int][]int64, len(userTraffic))\n\tfor i := range userTraffic {\n\t\tdata[userTraffic[i].UID] = []int64{userTraffic[i].Upload, userTraffic[i].Download}\n\t}\n\tconst path = \"/api/v1/server/UniProxy/push\"\n\tr, err := c.client.R().\n\t\tSetBody(data).\n\t\tForceContentType(\"application/json\").\n\t\tPost(path)\n\terr = c.checkResponse(r, path, err)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "api/panel/utils.go",
    "content": "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\nfunc (c *Client) Debug() {\n\tc.client.SetDebug(true)\n}\n\nfunc (c *Client) assembleURL(path string) string {\n\treturn path2.Join(c.APIHost + path)\n}\nfunc (c *Client) checkResponse(res *resty.Response, path string, err error) error {\n\tif err != nil {\n\t\treturn fmt.Errorf(\"request %s failed: %s\", c.assembleURL(path), err)\n\t}\n\tif res.StatusCode() >= 400 {\n\t\tbody := res.Body()\n\t\treturn fmt.Errorf(\"request %s failed: %s\", c.assembleURL(path), string(body))\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/action_linux.go",
    "content": "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\tstartCommand = cobra.Command{\n\t\tUse:   \"start\",\n\t\tShort: \"Start V2bX service\",\n\t\tRun:   startHandle,\n\t}\n\tstopCommand = cobra.Command{\n\t\tUse:   \"stop\",\n\t\tShort: \"Stop V2bX service\",\n\t\tRun:   stopHandle,\n\t}\n\trestartCommand = cobra.Command{\n\t\tUse:   \"restart\",\n\t\tShort: \"Restart V2bX service\",\n\t\tRun:   restartHandle,\n\t}\n\tlogCommand = cobra.Command{\n\t\tUse:   \"log\",\n\t\tShort: \"Output V2bX log\",\n\t\tRun: func(_ *cobra.Command, _ []string) {\n\t\t\texec.RunCommandStd(\"journalctl\", \"-u\", \"V2bX.service\", \"-e\", \"--no-pager\", \"-f\")\n\t\t},\n\t}\n)\n\nfunc init() {\n\tcommand.AddCommand(&startCommand)\n\tcommand.AddCommand(&stopCommand)\n\tcommand.AddCommand(&restartCommand)\n\tcommand.AddCommand(&logCommand)\n}\n\nfunc startHandle(_ *cobra.Command, _ []string) {\n\tr, err := checkRunning()\n\tif err != nil {\n\t\tfmt.Println(Err(\"check status error: \", err))\n\t\tfmt.Println(Err(\"V2bX启动失败\"))\n\t\treturn\n\t}\n\tif r {\n\t\tfmt.Println(Ok(\"V2bX已运行，无需再次启动，如需重启请选择重启\"))\n\t}\n\t_, err = exec.RunCommandByShell(\"systemctl start V2bX.service\")\n\tif err != nil {\n\t\tfmt.Println(Err(\"exec start cmd error: \", err))\n\t\tfmt.Println(Err(\"V2bX启动失败\"))\n\t\treturn\n\t}\n\ttime.Sleep(time.Second * 3)\n\tr, err = checkRunning()\n\tif err != nil {\n\t\tfmt.Println(Err(\"check status error: \", err))\n\t\tfmt.Println(Err(\"V2bX启动失败\"))\n\t}\n\tif !r {\n\t\tfmt.Println(Err(\"V2bX可能启动失败，请稍后使用 V2bX log 查看日志信息\"))\n\t\treturn\n\t}\n\tfmt.Println(Ok(\"V2bX 启动成功，请使用 V2bX log 查看运行日志\"))\n}\n\nfunc stopHandle(_ *cobra.Command, _ []string) {\n\t_, err := exec.RunCommandByShell(\"systemctl stop V2bX.service\")\n\tif err != nil {\n\t\tfmt.Println(Err(\"exec stop cmd error: \", err))\n\t\tfmt.Println(Err(\"V2bX停止失败\"))\n\t\treturn\n\t}\n\ttime.Sleep(2 * time.Second)\n\tr, err := checkRunning()\n\tif err != nil {\n\t\tfmt.Println(Err(\"check status error:\", err))\n\t\tfmt.Println(Err(\"V2bX停止失败\"))\n\t\treturn\n\t}\n\tif r {\n\t\tfmt.Println(Err(\"V2bX停止失败，可能是因为停止时间超过了两秒，请稍后查看日志信息\"))\n\t\treturn\n\t}\n\tfmt.Println(Ok(\"V2bX 停止成功\"))\n}\n\nfunc restartHandle(_ *cobra.Command, _ []string) {\n\t_, err := exec.RunCommandByShell(\"systemctl restart V2bX.service\")\n\tif err != nil {\n\t\tfmt.Println(Err(\"exec restart cmd error: \", err))\n\t\tfmt.Println(Err(\"V2bX重启失败\"))\n\t\treturn\n\t}\n\tr, err := checkRunning()\n\tif err != nil {\n\t\tfmt.Println(Err(\"check status error: \", err))\n\t\tfmt.Println(Err(\"V2bX重启失败\"))\n\t\treturn\n\t}\n\tif !r {\n\t\tfmt.Println(Err(\"V2bX可能启动失败，请稍后使用 V2bX log 查看日志信息\"))\n\t\treturn\n\t}\n\tfmt.Println(Ok(\"V2bX重启成功\"))\n}\n"
  },
  {
    "path": "cmd/cmd.go",
    "content": "package cmd\n\nimport (\n\tlog \"github.com/sirupsen/logrus\"\n\n\t_ \"github.com/Yuzuki616/V2bX/core/imports\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar command = &cobra.Command{\n\tUse: \"V2bX\",\n}\n\nfunc Run() {\n\terr := command.Execute()\n\tif err != nil {\n\t\tlog.WithField(\"err\", err).Error(\"Execute command failed\")\n\t}\n}\n"
  },
  {
    "path": "cmd/common.go",
    "content": "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\tgreen  = \"\\033[0;32m\"\n\tyellow = \"\\033[0;33m\"\n\tplain  = \"\\033[0m\"\n)\n\nfunc checkRunning() (bool, error) {\n\to, err := exec.RunCommandByShell(\"systemctl status V2bX | grep Active\")\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn strings.Contains(o, \"running\"), nil\n}\n\nfunc Err(msg ...any) string {\n\treturn red + fmt.Sprint(msg...) + plain\n}\n\nfunc Ok(msg ...any) string {\n\treturn green + fmt.Sprint(msg...) + plain\n}\n\nfunc Warn(msg ...any) string {\n\treturn yellow + fmt.Sprint(msg...) + plain\n}\n"
  },
  {
    "path": "cmd/common_test.go",
    "content": "package cmd\n\nimport \"testing\"\n\nfunc Test_printFailed(t *testing.T) {\n\tt.Log(Err(\"test\"))\n}\n"
  },
  {
    "path": "cmd/install_linux.go",
    "content": "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 targetVersion string\n\nvar (\n\tupdateCommand = cobra.Command{\n\t\tUse:   \"update\",\n\t\tShort: \"Update V2bX version\",\n\t\tRun: func(_ *cobra.Command, _ []string) {\n\t\t\texec.RunCommandStd(\"bash\",\n\t\t\t\t\"<(curl -Ls https://raw.githubusercontents.com/Yuzuki616/V2bX-script/master/install.sh)\",\n\t\t\t\ttargetVersion)\n\t\t},\n\t\tArgs: cobra.NoArgs,\n\t}\n\tuninstallCommand = cobra.Command{\n\t\tUse:   \"uninstall\",\n\t\tShort: \"Uninstall V2bX\",\n\t\tRun:   uninstallHandle,\n\t}\n)\n\nfunc init() {\n\tupdateCommand.PersistentFlags().StringVar(&targetVersion, \"version\", \"\", \"update target version\")\n\tcommand.AddCommand(&updateCommand)\n\tcommand.AddCommand(&uninstallCommand)\n}\n\nfunc uninstallHandle(_ *cobra.Command, _ []string) {\n\tvar yes string\n\tfmt.Println(Warn(\"确定要卸载 V2bX 吗?(Y/n)\"))\n\tfmt.Scan(&yes)\n\tif strings.ToLower(yes) != \"y\" {\n\t\tfmt.Println(\"已取消卸载\")\n\t}\n\t_, err := exec.RunCommandByShell(\"systemctl stop V2bX&&systemctl disable V2bX\")\n\tif err != nil {\n\t\tfmt.Println(Err(\"exec cmd error: \", err))\n\t\tfmt.Println(Err(\"卸载失败\"))\n\t\treturn\n\t}\n\t_ = os.RemoveAll(\"/etc/systemd/system/V2bX.service\")\n\t_ = os.RemoveAll(\"/etc/V2bX/\")\n\t_ = os.RemoveAll(\"/usr/local/V2bX/\")\n\t_ = os.RemoveAll(\"/bin/V2bX\")\n\t_, err = exec.RunCommandByShell(\"systemctl daemon-reload&&systemctl reset-failed\")\n\tif err != nil {\n\t\tfmt.Println(Err(\"exec cmd error: \", err))\n\t\tfmt.Println(Err(\"卸载失败\"))\n\t\treturn\n\t}\n\tfmt.Println(Ok(\"卸载成功\"))\n}\n"
  },
  {
    "path": "cmd/server.go",
    "content": "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/Yuzuki616/V2bX/core\"\n\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\t\"github.com/Yuzuki616/V2bX/limiter\"\n\t\"github.com/Yuzuki616/V2bX/node\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar (\n\tconfig string\n\twatch  bool\n)\n\nvar serverCommand = cobra.Command{\n\tUse:   \"server\",\n\tShort: \"Run V2bX server\",\n\tRun:   serverHandle,\n\tArgs:  cobra.NoArgs,\n}\n\nfunc init() {\n\tserverCommand.PersistentFlags().\n\t\tStringVarP(&config, \"config\", \"c\",\n\t\t\t\"/etc/V2bX/config.yml\", \"config file path\")\n\tserverCommand.PersistentFlags().\n\t\tBoolVarP(&watch, \"watch\", \"w\",\n\t\t\ttrue, \"watch file path change\")\n\tcommand.AddCommand(&serverCommand)\n}\n\nfunc serverHandle(_ *cobra.Command, _ []string) {\n\tshowVersion()\n\tc := conf.New()\n\terr := c.LoadFromPath(config)\n\tif err != nil {\n\t\tlog.WithField(\"err\", err).Error(\"Load config file failed\")\n\t\treturn\n\t}\n\tlimiter.Init()\n\tlog.Info(\"Start V2bX...\")\n\tvc, err := vCore.NewCore(&c.CoreConfig)\n\tif err != nil {\n\t\tlog.WithField(\"err\", err).Error(\"new core failed\")\n\t\treturn\n\t}\n\terr = vc.Start()\n\tif err != nil {\n\t\tlog.WithField(\"err\", err).Error(\"Start core failed\")\n\t\treturn\n\t}\n\tdefer vc.Close()\n\tnodes := node.New()\n\terr = nodes.Start(c.NodesConfig, vc)\n\tif err != nil {\n\t\tlog.WithField(\"err\", err).Error(\"Run nodes failed\")\n\t\treturn\n\t}\n\tdns := os.Getenv(\"XRAY_DNS_PATH\")\n\tif watch {\n\t\terr = c.Watch(config, dns, func() {\n\t\t\tnodes.Close()\n\t\t\terr = vc.Close()\n\t\t\tif err != nil {\n\t\t\t\tlog.WithField(\"err\", err).Error(\"Restart node failed\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tvc, err = vCore.NewCore(&c.CoreConfig)\n\t\t\tif err != nil {\n\t\t\t\tlog.WithField(\"err\", err).Error(\"New core failed\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\terr = vc.Start()\n\t\t\tif err != nil {\n\t\t\t\tlog.WithField(\"err\", err).Error(\"Start core failed\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\terr = nodes.Start(c.NodesConfig, vc)\n\t\t\tif err != nil {\n\t\t\t\tlog.WithField(\"err\", err).Error(\"Run nodes failed\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\truntime.GC()\n\t\t})\n\t\tif err != nil {\n\t\t\tlog.WithField(\"err\", err).Error(\"start watch failed\")\n\t\t\treturn\n\t\t}\n\t}\n\t// clear memory\n\truntime.GC()\n\t// wait exit signal\n\t{\n\t\tosSignals := make(chan os.Signal, 1)\n\t\tsignal.Notify(osSignals, syscall.SIGINT, syscall.SIGKILL, syscall.SIGTERM)\n\t\t<-osSignals\n\t}\n}\n"
  },
  {
    "path": "cmd/server_test.go",
    "content": "package cmd\n\nimport \"testing\"\n\nfunc TestRun(t *testing.T) {\n\tRun()\n}\n"
  },
  {
    "path": "cmd/synctime.go",
    "content": "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/cobra\"\n)\n\nvar ntpServer string\n\nvar commandSyncTime = &cobra.Command{\n\tUse:   \"synctime\",\n\tShort: \"Sync time from ntp server\",\n\tArgs:  cobra.NoArgs,\n\tRun:   synctimeHandle,\n}\n\nfunc init() {\n\tcommandSyncTime.Flags().StringVar(&ntpServer, \"server\", \"time.apple.com\", \"ntp server\")\n\tcommand.AddCommand(commandSyncTime)\n}\n\nfunc synctimeHandle(_ *cobra.Command, _ []string) {\n\tt, err := ntp.Time(ntpServer)\n\tif err != nil {\n\t\tfmt.Println(Err(\"get time from server error: \", err))\n\t\tfmt.Println(Err(\"同步时间失败\"))\n\t\treturn\n\t}\n\terr = systime.SetSystemTime(t)\n\tif err != nil {\n\t\tfmt.Println(Err(\"set system time error: \", err))\n\t\tfmt.Println(Err(\"同步时间失败\"))\n\t\treturn\n\t}\n\tfmt.Println(Ok(\"同步时间成功\"))\n}\n"
  },
  {
    "path": "cmd/version.go",
    "content": "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\tversion  = \"TempVersion\" //use ldflags replace\n\tcodename = \"V2bX\"\n\tintro    = \"A V2board backend based on multi core\"\n)\n\nvar versionCommand = cobra.Command{\n\tUse:   \"version\",\n\tShort: \"Print version info\",\n\tRun: func(_ *cobra.Command, _ []string) {\n\t\tshowVersion()\n\t},\n}\n\nfunc init() {\n\tcommand.AddCommand(&versionCommand)\n}\n\nfunc showVersion() {\n\tfmt.Printf(\"%s %s (%s) \\n\", codename, version, intro)\n\tfmt.Printf(\"Supported cores: %s\\n\", strings.Join(vCore.RegisteredCore(), \", \"))\n\t// Warning\n\tfmt.Println(Warn(\"This version need V2board version >= 1.7.0.\"))\n\tfmt.Println(Warn(\"The version have many changed for config, please check your config file\"))\n}\n"
  },
  {
    "path": "cmd/x25519.go",
    "content": "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\"github.com/spf13/cobra\"\n\t\"golang.org/x/crypto/curve25519\"\n)\n\nvar x25519Command = cobra.Command{\n\tUse:   \"x25519\",\n\tShort: \"Generate key pair for x25519 key exchange\",\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\texecuteX25519()\n\t},\n}\n\nfunc init() {\n\tcommand.AddCommand(&x25519Command)\n}\n\nfunc executeX25519() {\n\tvar output string\n\tvar err error\n\tdefer func() {\n\t\tfmt.Println(output)\n\t}()\n\tvar privateKey []byte\n\tvar publicKey []byte\n\tvar yes, key string\n\tfmt.Println(\"要基于节点信息生成密钥吗?(Y/n)\")\n\tfmt.Scan(&yes)\n\tif strings.ToLower(yes) == \"y\" {\n\t\tvar temp string\n\t\tfmt.Println(\"请输入节点id:\")\n\t\tfmt.Scan(&temp)\n\t\tkey = temp\n\t\tfmt.Println(\"请输入节点类型:\")\n\t\tfmt.Scan(&temp)\n\t\tkey += strings.ToLower(temp)\n\t\tfmt.Println(\"请输入Token:\")\n\t\tfmt.Scan(&temp)\n\t\tkey += temp\n\t\tprivateKey = crypt.GenX25519Private([]byte(key))\n\t} else {\n\t\tprivateKey = make([]byte, curve25519.ScalarSize)\n\t\tif _, err = rand.Read(privateKey); err != nil {\n\t\t\toutput = Err(\"read rand error: \", err)\n\t\t\treturn\n\t\t}\n\t}\n\tif publicKey, err = curve25519.X25519(privateKey, curve25519.Basepoint); err != nil {\n\t\toutput = Err(\"gen X25519 error: \", err)\n\t\treturn\n\t}\n\tp := base64.RawURLEncoding.EncodeToString(privateKey)\n\toutput = fmt.Sprint(\"Private key: \",\n\t\tp,\n\t\t\"\\nPublic key: \",\n\t\tbase64.RawURLEncoding.EncodeToString(publicKey))\n}\n"
  },
  {
    "path": "cmd/x25519_test.go",
    "content": "package cmd\n\nimport \"testing\"\n\nfunc Test_executeX25519(t *testing.T) {\n\texecuteX25519()\n}\n"
  },
  {
    "path": "common/crypt/aes.go",
    "content": "package crypt\n\nimport (\n\t\"crypto/aes\"\n\t\"encoding/base64\"\n)\n\nfunc AesEncrypt(data []byte, key []byte) (string, error) {\n\ta, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\ten := make([]byte, len(data))\n\ta.Encrypt(en, data)\n\treturn base64.StdEncoding.EncodeToString(en), nil\n}\n\nfunc AesDecrypt(data string, key []byte) (string, error) {\n\td, err := base64.StdEncoding.DecodeString(data)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\ta, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tde := make([]byte, len(data))\n\ta.Decrypt(de, d)\n\treturn string(de), nil\n}\n"
  },
  {
    "path": "common/crypt/x25519.go",
    "content": "package crypt\n\nimport (\n\t\"crypto/sha256\"\n)\n\nfunc GenX25519Private(data []byte) []byte {\n\tkey := sha256.Sum256(data)\n\tkey[0] &= 248\n\tkey[31] &= 127\n\tkey[31] |= 64\n\treturn key[:32]\n}\n"
  },
  {
    "path": "common/exec/exec.go",
    "content": "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.Command(\"bash\", \"-c\", cmd)\n\tout, err := e.CombinedOutput()\n\tif errors.Unwrap(err) == exec.ErrNotFound {\n\t\te = exec.Command(\"sh\", \"-c\", cmd)\n\t\tout, err = e.CombinedOutput()\n\t}\n\treturn string(out), err\n}\n\nfunc RunCommandStd(name string, args ...string) {\n\te := exec.Command(name, args...)\n\te.Stdout = os.Stdout\n\te.Stdin = os.Stdin\n\te.Stderr = os.Stderr\n\t_ = e.Run()\n}\n"
  },
  {
    "path": "common/file/file.go",
    "content": "package file\n\nimport \"os\"\n\nfunc IsExist(path string) bool {\n\t_, err := os.Stat(path)\n\treturn err == nil || !os.IsNotExist(err)\n}\n"
  },
  {
    "path": "common/format/user.go",
    "content": "package format\n\nimport (\n\t\"fmt\"\n)\n\nfunc UserTag(tag string, uuid string) string {\n\treturn fmt.Sprintf(\"%s|%s\", tag, uuid)\n}\n"
  },
  {
    "path": "common/rate/rate.go",
    "content": "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/common/buf\"\n)\n\ntype Writer struct {\n\twriter  buf.Writer\n\tlimiter *ratelimit.Bucket\n}\n\nfunc NewRateLimitWriter(writer buf.Writer, limiter *ratelimit.Bucket) buf.Writer {\n\treturn &Writer{\n\t\twriter:  writer,\n\t\tlimiter: limiter,\n\t}\n}\n\nfunc (w *Writer) Close() error {\n\treturn common.Close(w.writer)\n}\n\nfunc (w *Writer) WriteMultiBuffer(mb buf.MultiBuffer) error {\n\tw.limiter.Wait(int64(mb.Len()))\n\treturn w.writer.WriteMultiBuffer(mb)\n}\n"
  },
  {
    "path": "common/systime/time_stub.go",
    "content": "//go:build !(windows || linux || darwin)\n\npackage systime\n\nimport (\n\t\"os\"\n\t\"time\"\n)\n\nfunc SetSystemTime(nowTime time.Time) error {\n\treturn os.ErrInvalid\n}\n"
  },
  {
    "path": "common/systime/time_unix.go",
    "content": "//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 time.Time) error {\n\ttimeVal := unix.NsecToTimeval(nowTime.UnixNano())\n\treturn unix.Settimeofday(&timeVal)\n}\n"
  },
  {
    "path": "common/systime/time_windows.go",
    "content": "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 {\n\tvar systemTime windows.Systemtime\n\tsystemTime.Year = uint16(nowTime.Year())\n\tsystemTime.Month = uint16(nowTime.Month())\n\tsystemTime.Day = uint16(nowTime.Day())\n\tsystemTime.Hour = uint16(nowTime.Hour())\n\tsystemTime.Minute = uint16(nowTime.Minute())\n\tsystemTime.Second = uint16(nowTime.Second())\n\tsystemTime.Milliseconds = uint16(nowTime.UnixMilli() - nowTime.Unix()*1000)\n\n\tdllKernel32 := windows.NewLazySystemDLL(\"kernel32.dll\")\n\tproc := dllKernel32.NewProc(\"SetSystemTime\")\n\n\t_, _, err := proc.Call(\n\t\tuintptr(unsafe.Pointer(&systemTime)),\n\t)\n\n\tif err != nil && err.Error() != \"The operation completed successfully.\" {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "common/task/task.go",
    "content": "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 the task being run\n\tInterval time.Duration\n\t// Execute is the task function\n\tExecute func() error\n\n\taccess  sync.Mutex\n\ttimer   *time.Timer\n\trunning bool\n}\n\nfunc (t *Task) hasClosed() bool {\n\tt.access.Lock()\n\tdefer t.access.Unlock()\n\n\treturn !t.running\n}\n\nfunc (t *Task) checkedExecute(first bool) error {\n\tif t.hasClosed() {\n\t\treturn nil\n\t}\n\n\tt.access.Lock()\n\tdefer t.access.Unlock()\n\tif first {\n\t\tif err := t.Execute(); err != nil {\n\t\t\tt.running = false\n\t\t\treturn err\n\t\t}\n\t}\n\tif !t.running {\n\t\treturn nil\n\t}\n\tt.timer = time.AfterFunc(t.Interval, func() {\n\t\tt.checkedExecute(true)\n\t})\n\n\treturn nil\n}\n\n// Start implements common.Runnable.\nfunc (t *Task) Start(first bool) error {\n\tt.access.Lock()\n\tif t.running {\n\t\tt.access.Unlock()\n\t\treturn nil\n\t}\n\tt.running = true\n\tt.access.Unlock()\n\tif err := t.checkedExecute(first); err != nil {\n\t\tt.access.Lock()\n\t\tt.running = false\n\t\tt.access.Unlock()\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// Close implements common.Closable.\nfunc (t *Task) Close() {\n\tt.access.Lock()\n\tdefer t.access.Unlock()\n\n\tt.running = false\n\tif t.timer != nil {\n\t\tt.timer.Stop()\n\t\tt.timer = nil\n\t}\n}\n"
  },
  {
    "path": "common/task/task_test.go",
    "content": "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\tlog.Println(\"q\")\n\t\treturn nil\n\t}, Interval: time.Second}\n\tts.Start(false)\n}\n"
  },
  {
    "path": "conf/conf.go",
    "content": "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:\"CoreConfig\"`\n\tNodesConfig []*NodeConfig `yaml:\"Nodes\"`\n}\n\nfunc New() *Conf {\n\treturn &Conf{\n\t\tCoreConfig: CoreConfig{\n\t\t\tType: \"xray\",\n\t\t\tXrayConfig: &XrayConfig{\n\t\t\t\tLogConfig:          NewLogConfig(),\n\t\t\t\tAssetPath:          \"/etc/V2bX/\",\n\t\t\t\tDnsConfigPath:      \"\",\n\t\t\t\tInboundConfigPath:  \"\",\n\t\t\t\tOutboundConfigPath: \"\",\n\t\t\t\tRouteConfigPath:    \"\",\n\t\t\t\tConnectionConfig:   NewConnectionConfig(),\n\t\t\t},\n\t\t},\n\t\tNodesConfig: []*NodeConfig{},\n\t}\n}\n\nfunc (p *Conf) LoadFromPath(filePath string) error {\n\tf, err := os.Open(filePath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open config file error: %s\", err)\n\t}\n\tdefer f.Close()\n\tcontent, err := io.ReadAll(f)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"read file error: %s\", err)\n\t}\n\terr = yaml.Unmarshal(content, p)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"decode config error: %s\", err)\n\t}\n\told := &OldConfig{}\n\terr = yaml.Unmarshal(content, old)\n\tif err == nil {\n\t\tmigrateOldConfig(p, old)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "conf/conf_test.go",
    "content": "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(\"../example/config.yml.example\"))\n}\n\nfunc TestConf_Watch(t *testing.T) {\n\tc := New()\n\tlog.Println(c.Watch(\"../example/config.yml.example\", func() {\n\t\tlog.Println(1)\n\t}))\n\tselect {}\n}\n"
  },
  {
    "path": "conf/core.go",
    "content": "package conf\n\ntype CoreConfig struct {\n\tType       string      `yaml:\"Type\"`\n\tXrayConfig *XrayConfig `yaml:\"XrayConfig\"`\n}\n\ntype XrayConfig struct {\n\tLogConfig          *LogConfig        `yaml:\"Log\"`\n\tAssetPath          string            `yaml:\"AssetPath\"`\n\tDnsConfigPath      string            `yaml:\"DnsConfigPath\"`\n\tRouteConfigPath    string            `yaml:\"RouteConfigPath\"`\n\tConnectionConfig   *ConnectionConfig `yaml:\"ConnectionConfig\"`\n\tInboundConfigPath  string            `yaml:\"InboundConfigPath\"`\n\tOutboundConfigPath string            `yaml:\"OutboundConfigPath\"`\n}\n\ntype ConnectionConfig struct {\n\tHandshake    uint32 `yaml:\"handshake\"`\n\tConnIdle     uint32 `yaml:\"connIdle\"`\n\tUplinkOnly   uint32 `yaml:\"uplinkOnly\"`\n\tDownlinkOnly uint32 `yaml:\"downlinkOnly\"`\n\tBufferSize   int32  `yaml:\"bufferSize\"`\n}\n\nfunc NewConnectionConfig() *ConnectionConfig {\n\treturn &ConnectionConfig{\n\t\tHandshake:    4,\n\t\tConnIdle:     30,\n\t\tUplinkOnly:   2,\n\t\tDownlinkOnly: 4,\n\t\tBufferSize:   64,\n\t}\n}\n"
  },
  {
    "path": "conf/log.go",
    "content": "package conf\n\ntype LogConfig struct {\n\tLevel      string `yaml:\"Level\"`\n\tAccessPath string `yaml:\"AccessPath\"`\n\tErrorPath  string `yaml:\"ErrorPath\"`\n}\n\nfunc NewLogConfig() *LogConfig {\n\treturn &LogConfig{\n\t\tLevel:      \"warning\",\n\t\tAccessPath: \"\",\n\t\tErrorPath:  \"\",\n\t}\n}\n"
  },
  {
    "path": "conf/node.go",
    "content": "package conf\n\ntype NodeConfig struct {\n\tApiConfig        *ApiConfig        `yaml:\"ApiConfig\"`\n\tControllerConfig *ControllerConfig `yaml:\"ControllerConfig\"`\n}\n\ntype ApiConfig struct {\n\tAPIHost      string `yaml:\"ApiHost\"`\n\tNodeID       int    `yaml:\"NodeID\"`\n\tKey          string `yaml:\"ApiKey\"`\n\tNodeType     string `yaml:\"NodeType\"`\n\tTimeout      int    `yaml:\"Timeout\"`\n\tRuleListPath string `yaml:\"RuleListPath\"`\n}\n\ntype ControllerConfig struct {\n\tListenIP    string      `yaml:\"ListenIP\"`\n\tSendIP      string      `yaml:\"SendIP\"`\n\tXrayOptions XrayOptions `yaml:\"XrayOptions\"`\n\tHyOptions   HyOptions   `yaml:\"HyOptions\"`\n\tLimitConfig LimitConfig `yaml:\"LimitConfig\"`\n\tCertConfig  *CertConfig `yaml:\"CertConfig\"`\n}\n\ntype RealityConfig struct {\n\tDest         interface{} `yaml:\"Dest\" json:\"Dest\"`\n\tXver         uint64      `yaml:\"Xver\" json:\"Xver\"`\n\tServerNames  []string    `yaml:\"ServerNames\" json:\"ServerNames\"`\n\tPrivateKey   string      `yaml:\"PrivateKey\" json:\"PrivateKey\"`\n\tMinClientVer string      `yaml:\"MinClientVer\" json:\"MinClientVer\"`\n\tMaxClientVer string      `yaml:\"MaxClientVer\" json:\"MaxClientVer\"`\n\tMaxTimeDiff  uint64      `yaml:\"MaxTimeDiff\" json:\"MaxTimeDiff\"`\n\tShortIds     []string    `yaml:\"ShortIds\" json:\"ShortIds\"`\n}\n\ntype XrayOptions struct {\n\tEnableProxyProtocol bool             `yaml:\"EnableProxyProtocol\"`\n\tEnableDNS           bool             `yaml:\"EnableDNS\"`\n\tDNSType             string           `yaml:\"DNSType\"`\n\tEnableUot           bool             `yaml:\"EnableUot\"`\n\tEnableTFO           bool             `yaml:\"EnableTFO\"`\n\tDisableIVCheck      bool             `yaml:\"DisableIVCheck\"`\n\tDisableSniffing     bool             `yaml:\"DisableSniffing\"`\n\tEnableFallback      bool             `yaml:\"EnableFallback\"`\n\tFallBackConfigs     []FallBackConfig `yaml:\"FallBackConfigs\"`\n}\n\ntype HyOptions struct {\n\tResolver          string `yaml:\"Resolver\"`\n\tResolvePreference string `yaml:\"ResolvePreference\"`\n\tSendDevice        string `yaml:\"SendDevice\"`\n}\n\ntype LimitConfig struct {\n\tEnableRealtime          bool                     `yaml:\"EnableRealtime\"`\n\tSpeedLimit              int                      `yaml:\"SpeedLimit\"`\n\tIPLimit                 int                      `yaml:\"DeviceLimit\"`\n\tConnLimit               int                      `yaml:\"ConnLimit\"`\n\tEnableIpRecorder        bool                     `yaml:\"EnableIpRecorder\"`\n\tIpRecorderConfig        *IpReportConfig          `yaml:\"IpRecorderConfig\"`\n\tEnableDynamicSpeedLimit bool                     `yaml:\"EnableDynamicSpeedLimit\"`\n\tDynamicSpeedLimitConfig *DynamicSpeedLimitConfig `yaml:\"DynamicSpeedLimitConfig\"`\n}\n\ntype FallBackConfig struct {\n\tSNI              string `yaml:\"SNI\"`\n\tAlpn             string `yaml:\"Alpn\"`\n\tPath             string `yaml:\"Path\"`\n\tDest             string `yaml:\"Dest\"`\n\tProxyProtocolVer uint64 `yaml:\"ProxyProtocolVer\"`\n}\n\ntype RecorderConfig struct {\n\tUrl     string `yaml:\"Url\"`\n\tToken   string `yaml:\"Token\"`\n\tTimeout int    `yaml:\"Timeout\"`\n}\n\ntype RedisConfig struct {\n\tAddress  string `yaml:\"Address\"`\n\tPassword string `yaml:\"Password\"`\n\tDb       int    `yaml:\"Db\"`\n\tExpiry   int    `json:\"Expiry\"`\n}\n\ntype IpReportConfig struct {\n\tPeriodic       int             `yaml:\"Periodic\"`\n\tType           string          `yaml:\"Type\"`\n\tRecorderConfig *RecorderConfig `yaml:\"RecorderConfig\"`\n\tRedisConfig    *RedisConfig    `yaml:\"RedisConfig\"`\n\tEnableIpSync   bool            `yaml:\"EnableIpSync\"`\n}\n\ntype DynamicSpeedLimitConfig struct {\n\tPeriodic   int   `yaml:\"Periodic\"`\n\tTraffic    int64 `yaml:\"Traffic\"`\n\tSpeedLimit int   `yaml:\"SpeedLimit\"`\n\tExpireTime int   `yaml:\"ExpireTime\"`\n}\n\ntype CertConfig struct {\n\tCertMode         string            `yaml:\"CertMode\"` // none, file, http, dns\n\tRejectUnknownSni bool              `yaml:\"RejectUnknownSni\"`\n\tCertDomain       string            `yaml:\"CertDomain\"`\n\tCertFile         string            `yaml:\"CertFile\"`\n\tKeyFile          string            `yaml:\"KeyFile\"`\n\tProvider         string            `yaml:\"Provider\"` // alidns, cloudflare, gandi, godaddy....\n\tEmail            string            `yaml:\"Email\"`\n\tDNSEnv           map[string]string `yaml:\"DNSEnv\"`\n\tRealityConfig    *RealityConfig    `yaml:\"RealityConfig\"`\n}\n"
  },
  {
    "path": "conf/old.go",
    "content": "package conf\n\nimport \"log\"\n\ntype OldConfig struct {\n\tNodesConfig []*struct {\n\t\tApiConfig        *OldApiConfig        `yaml:\"ApiConfig\"`\n\t\tControllerConfig *OldControllerConfig `yaml:\"ControllerConfig\"`\n\t} `yaml:\"Nodes\"`\n}\n\ntype OldControllerConfig struct {\n\tListenIP                string                   `yaml:\"ListenIP\"`\n\tSendIP                  string                   `yaml:\"SendIP\"`\n\tEnableDNS               bool                     `yaml:\"EnableDNS\"`\n\tDNSType                 string                   `yaml:\"DNSType\"`\n\tDisableUploadTraffic    bool                     `yaml:\"DisableUploadTraffic\"`\n\tDisableGetRule          bool                     `yaml:\"DisableGetRule\"`\n\tEnableProxyProtocol     bool                     `yaml:\"EnableProxyProtocol\"`\n\tEnableFallback          bool                     `yaml:\"EnableFallback\"`\n\tDisableIVCheck          bool                     `yaml:\"DisableIVCheck\"`\n\tDisableSniffing         bool                     `yaml:\"DisableSniffing\"`\n\tFallBackConfigs         []*FallBackConfig        `yaml:\"FallBackConfigs\"`\n\tEnableIpRecorder        bool                     `yaml:\"EnableIpRecorder\"`\n\tIpRecorderConfig        *IpReportConfig          `yaml:\"IpRecorderConfig\"`\n\tEnableDynamicSpeedLimit bool                     `yaml:\"EnableDynamicSpeedLimit\"`\n\tDynamicSpeedLimitConfig *DynamicSpeedLimitConfig `yaml:\"DynamicSpeedLimitConfig\"`\n\tCertConfig              *CertConfig              `yaml:\"CertConfig\"`\n}\n\ntype OldApiConfig struct {\n\tAPIHost             string `yaml:\"ApiHost\"`\n\tNodeID              int    `yaml:\"NodeID\"`\n\tKey                 string `yaml:\"ApiKey\"`\n\tNodeType            string `yaml:\"NodeType\"`\n\tEnableVless         bool   `yaml:\"EnableVless\"`\n\tTimeout             int    `yaml:\"Timeout\"`\n\tSpeedLimit          int    `yaml:\"SpeedLimit\"`\n\tDeviceLimit         int    `yaml:\"DeviceLimit\"`\n\tRuleListPath        string `yaml:\"RuleListPath\"`\n\tDisableCustomConfig bool   `yaml:\"DisableCustomConfig\"`\n}\n\nfunc migrateOldConfig(c *Conf, old *OldConfig) {\n\tchanged := false\n\tfor i, n := range c.NodesConfig {\n\t\tif i >= len(old.NodesConfig) {\n\t\t\tbreak\n\t\t}\n\t\t// limit config\n\t\tif old.NodesConfig[i].ApiConfig.SpeedLimit != 0 {\n\t\t\tn.ControllerConfig.LimitConfig.SpeedLimit = old.NodesConfig[i].ApiConfig.SpeedLimit\n\t\t\tchanged = true\n\t\t}\n\t\tif old.NodesConfig[i].ApiConfig.DeviceLimit != 0 {\n\t\t\tn.ControllerConfig.LimitConfig.IPLimit = old.NodesConfig[i].ApiConfig.DeviceLimit\n\t\t\tchanged = true\n\t\t}\n\t\tif old.NodesConfig[i].ControllerConfig.EnableDynamicSpeedLimit {\n\t\t\tn.ControllerConfig.LimitConfig.EnableDynamicSpeedLimit = true\n\t\t\tchanged = true\n\t\t}\n\t\tif old.NodesConfig[i].ControllerConfig.DynamicSpeedLimitConfig != nil {\n\t\t\tn.ControllerConfig.LimitConfig.DynamicSpeedLimitConfig =\n\t\t\t\told.NodesConfig[i].ControllerConfig.DynamicSpeedLimitConfig\n\t\t\tchanged = true\n\t\t}\n\t\tif old.NodesConfig[i].ControllerConfig.EnableIpRecorder {\n\t\t\tn.ControllerConfig.LimitConfig.EnableIpRecorder = true\n\t\t\tchanged = true\n\t\t}\n\t\tif old.NodesConfig[i].ControllerConfig.IpRecorderConfig != nil {\n\t\t\tn.ControllerConfig.LimitConfig.IpRecorderConfig =\n\t\t\t\told.NodesConfig[i].ControllerConfig.IpRecorderConfig\n\t\t\tchanged = true\n\t\t}\n\t}\n\tif changed {\n\t\tlog.Println(\"Warning: This config file is old.\")\n\t}\n}\n"
  },
  {
    "path": "conf/watch.go",
    "content": "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, dnsPath string, reload func()) error {\n\twatcher, err := fsnotify.NewWatcher()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new watcher error: %s\", err)\n\t}\n\tgo func() {\n\t\tvar pre time.Time\n\t\tdefer watcher.Close()\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase e := <-watcher.Events:\n\t\t\t\tif e.Has(fsnotify.Chmod) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif pre.Add(10 * time.Second).After(time.Now()) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tpre = time.Now()\n\t\t\t\tgo func() {\n\t\t\t\t\ttime.Sleep(10 * time.Second)\n\t\t\t\t\tif e.Name == dnsPath {\n\t\t\t\t\t\tlog.Println(\"DNS file changed, reloading...\")\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog.Println(\"config dir changed, reloading...\")\n\t\t\t\t\t}\n\t\t\t\t\t*p = *New()\n\t\t\t\t\terr := p.LoadFromPath(filePath)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tlog.Printf(\"reload config error: %s\", err)\n\t\t\t\t\t}\n\t\t\t\t\treload()\n\t\t\t\t\tlog.Println(\"reload config success\")\n\t\t\t\t}()\n\t\t\tcase err := <-watcher.Errors:\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Printf(\"File watcher error: %s\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\terr = watcher.Add(path.Dir(filePath))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"watch file error: %s\", err)\n\t}\n\tif dnsPath != \"\" {\n\t\terr = watcher.Add(path.Dir(dnsPath))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"watch dns file error: %s\", err)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/core.go",
    "content": "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.CoreConfig) (Core, error){}\n)\n\nfunc NewCore(c *conf.CoreConfig) (Core, error) {\n\t// multi core\n\tif types := strings.Split(c.Type, \" \"); len(types) > 1 {\n\t\tvar cs []Core\n\t\tfor _, t := range types {\n\t\t\tf, ok := cores[strings.ToLower(t)]\n\t\t\tif !ok {\n\t\t\t\treturn nil, errors.New(\"unknown core type: \" + t)\n\t\t\t}\n\t\t\tcore1, err := f(c)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tcs = append(cs, core1)\n\t\t}\n\t\treturn &Selector{\n\t\t\tcores: cs,\n\t\t}, nil\n\t}\n\t// one core\n\tif f, ok := cores[strings.ToLower(c.Type)]; ok {\n\t\treturn f(c)\n\t} else {\n\t\treturn nil, errors.New(\"unknown core type\")\n\t}\n}\n\nfunc RegisterCore(t string, f func(c *conf.CoreConfig) (Core, error)) {\n\tcores[t] = f\n}\n\nfunc RegisteredCore() []string {\n\tcs := make([]string, 0, len(cores))\n\tfor k := range cores {\n\t\tcs = append(cs, k)\n\t}\n\treturn cs\n}\n"
  },
  {
    "path": "core/hy/config.go",
    "content": "package hy\n\nconst (\n\tmbpsToBps   = 125000\n\tminSpeedBPS = 16384\n\n\tDefaultALPN = \"hysteria\"\n\n\tDefaultStreamReceiveWindow     = 16777216                           // 16 MB\n\tDefaultConnectionReceiveWindow = DefaultStreamReceiveWindow * 5 / 2 // 40 MB\n\n\tDefaultMaxIncomingStreams = 1024\n\n\tDefaultMMDBFilename = \"GeoLite2-Country.mmdb\"\n\n\tServerMaxIdleTimeoutSec     = 60\n\tDefaultClientIdleTimeoutSec = 20\n\n\tDefaultClientHopIntervalSec = 10\n)\n\nfunc SpeedTrans(upM, downM int) (uint64, uint64) {\n\tup := uint64(upM) * mbpsToBps\n\tdown := uint64(downM) * mbpsToBps\n\treturn up, down\n}\n"
  },
  {
    "path": "core/hy/counter.go",
    "content": "package hy\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\ntype UserTrafficCounter struct {\n\tcounters map[string]*counters\n\tlock     sync.RWMutex\n}\n\ntype counters struct {\n\tUpCounter   atomic.Int64\n\tDownCounter atomic.Int64\n\t//ConnGauge   atomic.Int64\n}\n\nfunc NewUserTrafficCounter() *UserTrafficCounter {\n\treturn &UserTrafficCounter{\n\t\tcounters: map[string]*counters{},\n\t}\n}\n\nfunc (c *UserTrafficCounter) getCounters(auth string) *counters {\n\tc.lock.RLock()\n\tcts, ok := c.counters[auth]\n\tc.lock.RUnlock()\n\tif !ok {\n\t\tcts = &counters{}\n\t\tc.counters[auth] = cts\n\t}\n\treturn cts\n}\n\nfunc (c *UserTrafficCounter) Rx(auth string, n int) {\n\tcts := c.getCounters(auth)\n\tcts.DownCounter.Add(int64(n))\n}\n\nfunc (c *UserTrafficCounter) Tx(auth string, n int) {\n\tcts := c.getCounters(auth)\n\tcts.UpCounter.Add(int64(n))\n}\n\nfunc (c *UserTrafficCounter) IncConn(_ string) {\n\t/*cts := c.getCounters(auth)\n\tcts.ConnGauge.Add(1)*/\n\treturn\n}\n\nfunc (c *UserTrafficCounter) DecConn(_ string) {\n\t/*cts := c.getCounters(auth)\n\tcts.ConnGauge.Add(1)*/\n\treturn\n}\n\nfunc (c *UserTrafficCounter) Reset(auth string) {\n\tcts := c.getCounters(auth)\n\tcts.UpCounter.Store(0)\n\tcts.DownCounter.Store(0)\n}\n\nfunc (c *UserTrafficCounter) Delete(auth string) {\n\tc.lock.Lock()\n\tdelete(c.counters, auth)\n\tc.lock.Unlock()\n}\n"
  },
  {
    "path": "core/hy/counter_test.go",
    "content": "package hy\n\nimport \"testing\"\n\nfunc TestUserTrafficCounter_Rx(t *testing.T) {\n\n}\n"
  },
  {
    "path": "core/hy/hy.go",
    "content": "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.com/hashicorp/go-multierror\"\n)\n\nfunc init() {\n\tvCore.RegisterCore(\"hy\", NewHy)\n}\n\ntype Hy struct {\n\tservers sync.Map\n}\n\nfunc NewHy(_ *conf.CoreConfig) (vCore.Core, error) {\n\treturn &Hy{\n\t\tservers: sync.Map{},\n\t}, nil\n}\n\nfunc (h *Hy) Start() error {\n\treturn nil\n}\n\nfunc (h *Hy) Close() error {\n\tvar errs error\n\th.servers.Range(func(tag, s any) bool {\n\t\terr := s.(*Server).Close()\n\t\tif err != nil {\n\t\t\terrs = multierror.Append(errs, fmt.Errorf(\"close %s error: %s\", tag, err))\n\t\t}\n\t\treturn true\n\t})\n\tif errs != nil {\n\t\treturn errs\n\t}\n\treturn nil\n}\n\nfunc (h *Hy) Protocols() []string {\n\treturn []string{\n\t\t\"hysteria\",\n\t}\n}\n"
  },
  {
    "path": "core/hy/ipmasker.go",
    "content": "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 address with the configured CIDR.\n// addr can be \"host:port\" or just host.\nfunc (m *ipMasker) Mask(addr string) string {\n\tif m.IPv4Mask == nil && m.IPv6Mask == nil {\n\t\treturn addr\n\t}\n\n\thost, port, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\t// just host\n\t\thost, port = addr, \"\"\n\t}\n\tip := net.ParseIP(host)\n\tif ip == nil {\n\t\t// not an IP address, return as is\n\t\treturn addr\n\t}\n\tif ip4 := ip.To4(); ip4 != nil && m.IPv4Mask != nil {\n\t\t// IPv4\n\t\thost = ip4.Mask(m.IPv4Mask).String()\n\t} else if ip6 := ip.To16(); ip6 != nil && m.IPv6Mask != nil {\n\t\t// IPv6\n\t\thost = ip6.Mask(m.IPv6Mask).String()\n\t}\n\tif port != \"\" {\n\t\treturn net.JoinHostPort(host, port)\n\t} else {\n\t\treturn host\n\t}\n}\n\nvar defaultIPMasker = &ipMasker{}\n"
  },
  {
    "path": "core/hy/kploader.go",
    "content": "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 keypairLoader struct {\n\tcertMu   sync.RWMutex\n\tcert     *tls.Certificate\n\tcertPath string\n\tkeyPath  string\n}\n\nfunc newKeypairLoader(certPath, keyPath string) (*keypairLoader, error) {\n\tloader := &keypairLoader{\n\t\tcertPath: certPath,\n\t\tkeyPath:  keyPath,\n\t}\n\tcert, err := tls.LoadX509KeyPair(certPath, keyPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tloader.cert = &cert\n\twatcher, err := fsnotify.NewWatcher()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase event, ok := <-watcher.Events:\n\t\t\t\tif !ok {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tswitch event.Op {\n\t\t\t\tcase fsnotify.Create, fsnotify.Write, fsnotify.Rename, fsnotify.Chmod:\n\t\t\t\t\tlogrus.WithFields(logrus.Fields{\n\t\t\t\t\t\t\"file\": event.Name,\n\t\t\t\t\t}).Info(\"Keypair change detected, reloading...\")\n\t\t\t\t\tif err := loader.load(); err != nil {\n\t\t\t\t\t\tlogrus.WithFields(logrus.Fields{\n\t\t\t\t\t\t\t\"error\": err,\n\t\t\t\t\t\t}).Error(\"Failed to reload keypair\")\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogrus.Info(\"Keypair successfully reloaded\")\n\t\t\t\t\t}\n\t\t\t\tcase fsnotify.Remove:\n\t\t\t\t\t_ = watcher.Add(event.Name) // Workaround for vim\n\t\t\t\t\t// https://github.com/fsnotify/fsnotify/issues/92\n\t\t\t\t}\n\t\t\tcase err, ok := <-watcher.Errors:\n\t\t\t\tif !ok {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tlogrus.WithFields(logrus.Fields{\n\t\t\t\t\t\"error\": err,\n\t\t\t\t}).Error(\"Failed to watch keypair files for changes\")\n\t\t\t}\n\t\t}\n\t}()\n\terr = watcher.Add(certPath)\n\tif err != nil {\n\t\t_ = watcher.Close()\n\t\treturn nil, err\n\t}\n\terr = watcher.Add(keyPath)\n\tif err != nil {\n\t\t_ = watcher.Close()\n\t\treturn nil, err\n\t}\n\treturn loader, nil\n}\n\nfunc (kpr *keypairLoader) load() error {\n\tcert, err := tls.LoadX509KeyPair(kpr.certPath, kpr.keyPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tkpr.certMu.Lock()\n\tkpr.cert = &cert\n\tkpr.certMu.Unlock()\n\treturn nil\n}\n\nfunc (kpr *keypairLoader) GetCertificateFunc() func(*tls.ClientHelloInfo) (*tls.Certificate, error) {\n\treturn func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\tkpr.certMu.RLock()\n\t\tdefer kpr.certMu.RUnlock()\n\t\treturn kpr.cert, nil\n\t}\n}\n"
  },
  {
    "path": "core/hy/mmdb.go",
    "content": "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(filename string) (*geoip2.Reader, error) {\n\tif _, err := os.Stat(filename); err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\tlogrus.Info(\"GeoLite2 database not found, downloading...\")\n\t\t\tlogrus.WithFields(logrus.Fields{\n\t\t\t\t\"file\": filename,\n\t\t\t}).Info(\"GeoLite2 database downloaded\")\n\t\t\treturn geoip2.Open(filename)\n\t\t} else {\n\t\t\t// some other error\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\t// file exists, just open it\n\t\treturn geoip2.Open(filename)\n\t}\n}\n"
  },
  {
    "path": "core/hy/node.go",
    "content": "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.com/Yuzuki616/V2bX/limiter\"\n)\n\nfunc (h *Hy) AddNode(tag string, info *panel.NodeInfo, c *conf.ControllerConfig) error {\n\tif info.Type != \"hysteria\" {\n\t\treturn errors.New(\"the core not support \" + info.Type)\n\t}\n\tswitch c.CertConfig.CertMode {\n\tcase \"reality\", \"none\", \"\":\n\t\treturn errors.New(\"hysteria need normal tls cert\")\n\t}\n\tl, err := limiter.GetLimiter(tag)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get limiter error: %s\", err)\n\t}\n\ts := NewServer(tag, l)\n\terr = s.runServer(info, c)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"run hy server error: %s\", err)\n\t}\n\th.servers.Store(tag, s)\n\treturn nil\n}\n\nfunc (h *Hy) DelNode(tag string) error {\n\tif s, e := h.servers.Load(tag); e {\n\t\terr := s.(*Server).Close()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\th.servers.Delete(tag)\n\t\treturn nil\n\t}\n\treturn errors.New(\"the node is not have\")\n}\n"
  },
  {
    "path": "core/hy/resolver.go",
    "content": "package hy\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"github.com/Yuzuki616/hysteria/core/utils\"\n\trdns \"github.com/folbricht/routedns\"\n\t\"net\"\n\t\"net/url\"\n\t\"strings\"\n)\n\nvar errInvalidSyntax = errors.New(\"invalid syntax\")\n\nfunc setResolver(dns string) error {\n\tif net.ParseIP(dns) != nil {\n\t\t// Just an IP address, treat as UDP 53\n\t\tdns = \"udp://\" + net.JoinHostPort(dns, \"53\")\n\t}\n\tvar r rdns.Resolver\n\tif strings.HasPrefix(dns, \"udp://\") {\n\t\t// Standard UDP DNS resolver\n\t\tdns = strings.TrimPrefix(dns, \"udp://\")\n\t\tif dns == \"\" {\n\t\t\treturn errInvalidSyntax\n\t\t}\n\t\tif _, _, err := utils.SplitHostPort(dns); err != nil {\n\t\t\t// Append the default DNS port\n\t\t\tdns = net.JoinHostPort(dns, \"53\")\n\t\t}\n\t\tclient, err := rdns.NewDNSClient(\"dns-udp\", dns, \"udp\", rdns.DNSClientOptions{})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tr = client\n\t} else if strings.HasPrefix(dns, \"tcp://\") {\n\t\t// Standard TCP DNS resolver\n\t\tdns = strings.TrimPrefix(dns, \"tcp://\")\n\t\tif dns == \"\" {\n\t\t\treturn errInvalidSyntax\n\t\t}\n\t\tif _, _, err := utils.SplitHostPort(dns); err != nil {\n\t\t\t// Append the default DNS port\n\t\t\tdns = net.JoinHostPort(dns, \"53\")\n\t\t}\n\t\tclient, err := rdns.NewDNSClient(\"dns-tcp\", dns, \"tcp\", rdns.DNSClientOptions{})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tr = client\n\t} else if strings.HasPrefix(dns, \"https://\") {\n\t\t// DoH resolver\n\t\tif dohURL, err := url.Parse(dns); err != nil {\n\t\t\treturn err\n\t\t} else {\n\t\t\t// Need to set bootstrap address to avoid loopback DNS lookup\n\t\t\tdohIPAddr, err := net.ResolveIPAddr(\"ip\", dohURL.Hostname())\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tclient, err := rdns.NewDoHClient(\"doh\", dns, rdns.DoHClientOptions{\n\t\t\t\tBootstrapAddr: dohIPAddr.String(),\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tr = client\n\t\t}\n\t} else if strings.HasPrefix(dns, \"tls://\") {\n\t\t// DoT resolver\n\t\tdns = strings.TrimPrefix(dns, \"tls://\")\n\t\tif dns == \"\" {\n\t\t\treturn errInvalidSyntax\n\t\t}\n\t\tdotHost, _, err := utils.SplitHostPort(dns)\n\t\tif err != nil {\n\t\t\t// Append the default DNS port\n\t\t\tdns = net.JoinHostPort(dns, \"853\")\n\t\t}\n\t\t// Need to set bootstrap address to avoid loopback DNS lookup\n\t\tdotIPAddr, err := net.ResolveIPAddr(\"ip\", dotHost)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tclient, err := rdns.NewDoTClient(\"dot\", dns, rdns.DoTClientOptions{\n\t\t\tBootstrapAddr: dotIPAddr.String(),\n\t\t\tTLSConfig:     new(tls.Config),\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tr = client\n\t} else if strings.HasPrefix(dns, \"quic://\") {\n\t\t// DoQ resolver\n\t\tdns = strings.TrimPrefix(dns, \"quic://\")\n\t\tif dns == \"\" {\n\t\t\treturn errInvalidSyntax\n\t\t}\n\t\tdoqHost, _, err := utils.SplitHostPort(dns)\n\t\tif err != nil {\n\t\t\t// Append the default DNS port\n\t\t\tdns = net.JoinHostPort(dns, \"853\")\n\t\t}\n\t\t// Need to set bootstrap address to avoid loopback DNS lookup\n\t\tdoqIPAddr, err := net.ResolveIPAddr(\"ip\", doqHost)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tclient, err := rdns.NewDoQClient(\"doq\", dns, rdns.DoQClientOptions{\n\t\t\tBootstrapAddr: doqIPAddr.String(),\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tr = client\n\t} else {\n\t\treturn errInvalidSyntax\n\t}\n\tcache := rdns.NewCache(\"cache\", r, rdns.CacheOptions{})\n\tnet.DefaultResolver = rdns.NewNetResolver(cache)\n\treturn nil\n}\n"
  },
  {
    "path": "core/hy/server.go",
    "content": "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/panel\"\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\t\"github.com/Yuzuki616/V2bX/limiter\"\n\t\"github.com/Yuzuki616/hysteria/core/sockopt\"\n\t\"github.com/Yuzuki616/quic-go\"\n\n\t\"github.com/Yuzuki616/hysteria/core/acl\"\n\t\"github.com/Yuzuki616/hysteria/core/cs\"\n\t\"github.com/Yuzuki616/hysteria/core/pktconns\"\n\t\"github.com/Yuzuki616/hysteria/core/pmtud\"\n\t\"github.com/Yuzuki616/hysteria/core/transport\"\n\t\"github.com/sirupsen/logrus\"\n)\n\nvar serverPacketConnFuncFactoryMap = map[string]pktconns.ServerPacketConnFuncFactory{\n\t\"\":             pktconns.NewServerUDPConnFunc,\n\t\"udp\":          pktconns.NewServerUDPConnFunc,\n\t\"wechat\":       pktconns.NewServerWeChatConnFunc,\n\t\"wechat-video\": pktconns.NewServerWeChatConnFunc,\n\t\"faketcp\":      pktconns.NewServerFakeTCPConnFunc,\n}\n\ntype Server struct {\n\ttag     string\n\tl       *limiter.Limiter\n\tcounter *UserTrafficCounter\n\tusers   sync.Map\n\trunning atomic.Bool\n\t*cs.Server\n}\n\nfunc NewServer(tag string, l *limiter.Limiter) *Server {\n\treturn &Server{\n\t\ttag: tag,\n\t\tl:   l,\n\t}\n}\n\nfunc (s *Server) runServer(node *panel.NodeInfo, c *conf.ControllerConfig) error {\n\t/*if c.HyOptions == nil {\n\t\treturn errors.New(\"hy options is not vail\")\n\t}*/\n\t// Resolver\n\tif len(c.HyOptions.Resolver) > 0 {\n\t\terr := setResolver(c.HyOptions.Resolver)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"set resolver error: %s\", err)\n\t\t}\n\t}\n\t// tls config\n\tkpl, err := newKeypairLoader(c.CertConfig.CertFile, c.CertConfig.KeyFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"load cert error: %s\", err)\n\t}\n\ttlsConfig := &tls.Config{\n\t\tGetCertificate: kpl.GetCertificateFunc(),\n\t\tNextProtos:     []string{DefaultALPN},\n\t\tMinVersion:     tls.VersionTLS13,\n\t}\n\t// QUIC config\n\tquicConfig := &quic.Config{\n\t\tInitialStreamReceiveWindow:     DefaultStreamReceiveWindow,\n\t\tMaxStreamReceiveWindow:         DefaultStreamReceiveWindow,\n\t\tInitialConnectionReceiveWindow: DefaultConnectionReceiveWindow,\n\t\tMaxConnectionReceiveWindow:     DefaultConnectionReceiveWindow,\n\t\tMaxIncomingStreams:             int64(DefaultMaxIncomingStreams),\n\t\tMaxIdleTimeout:                 ServerMaxIdleTimeoutSec * time.Second,\n\t\tKeepAlivePeriod:                0, // Keep alive should solely be client's responsibility\n\t\tDisablePathMTUDiscovery:        false,\n\t\tEnableDatagrams:                true,\n\t}\n\tif !quicConfig.DisablePathMTUDiscovery && pmtud.DisablePathMTUDiscovery {\n\t\tlogrus.Info(\"Path MTU Discovery is not yet supported on this platform\")\n\t}\n\t// Resolve preference\n\tif len(c.HyOptions.ResolvePreference) > 0 {\n\t\tpref, err := transport.ResolvePreferenceFromString(c.HyOptions.Resolver)\n\t\tif err != nil {\n\t\t\tlogrus.WithFields(logrus.Fields{\n\t\t\t\t\"error\": err,\n\t\t\t}).Fatal(\"Failed to parse the resolve preference\")\n\t\t}\n\t\ttransport.DefaultServerTransport.ResolvePreference = pref\n\t}\n\t/*// SOCKS5 outbound\n\tif config.SOCKS5Outbound.Server != \"\" {\n\t\ttransport.DefaultServerTransport.SOCKS5Client = transport.NewSOCKS5Client(config.SOCKS5Outbound.Server,\n\t\t\tconfig.SOCKS5Outbound.User, config.SOCKS5Outbound.Password)\n\t}*/\n\t// Bind outbound\n\tif c.HyOptions.SendDevice != \"\" {\n\t\tiface, err := net.InterfaceByName(c.HyOptions.SendDevice)\n\t\tif err != nil {\n\t\t\tlogrus.WithFields(logrus.Fields{\n\t\t\t\t\"error\": err,\n\t\t\t}).Fatal(\"Failed to find the interface\")\n\t\t}\n\t\ttransport.DefaultServerTransport.LocalUDPIntf = iface\n\t\tsockopt.BindDialer(transport.DefaultServerTransport.Dialer, iface)\n\t}\n\tif c.SendIP != \"\" {\n\t\tip := net.ParseIP(c.SendIP)\n\t\tif ip == nil {\n\t\t\tlogrus.WithFields(logrus.Fields{\n\t\t\t\t\"error\": err,\n\t\t\t}).Fatal(\"Failed to parse the address\")\n\t\t}\n\t\ttransport.DefaultServerTransport.Dialer.LocalAddr = &net.TCPAddr{IP: ip}\n\t\ttransport.DefaultServerTransport.LocalUDPAddr = &net.UDPAddr{IP: ip}\n\t}\n\t// ACL\n\tvar aclEngine *acl.Engine\n\t// Prometheus\n\ts.counter = NewUserTrafficCounter()\n\t// Packet conn\n\tpktConnFuncFactory := serverPacketConnFuncFactoryMap[\"\"]\n\tif pktConnFuncFactory == nil {\n\t\treturn fmt.Errorf(\"unsopport protocol\")\n\t}\n\tpktConnFunc := pktConnFuncFactory(node.HyObfs)\n\taddr := fmt.Sprintf(\"%s:%d\", c.ListenIP, node.Port)\n\tpktConn, err := pktConnFunc(addr)\n\tif err != nil {\n\t\tlogrus.WithFields(logrus.Fields{\n\t\t\t\"error\": err,\n\t\t\t\"addr\":  addr,\n\t\t}).Fatal(\"Failed to listen on the UDP address\")\n\t}\n\t// Server\n\tup, down := SpeedTrans(node.UpMbps, node.DownMbps)\n\ts.Server, err = cs.NewServer(tlsConfig, quicConfig, pktConn,\n\t\ttransport.DefaultServerTransport, up, down, false, aclEngine,\n\t\ts.connectFunc, s.disconnectFunc, tcpRequestFunc, tcpErrorFunc, udpRequestFunc, udpErrorFunc, s.counter)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new server error: %s\", err)\n\t}\n\tlogrus.WithField(\"addr\", addr).Info(\"Server up and running\")\n\tgo func() {\n\t\ts.running.Store(true)\n\t\tdefer func() {\n\t\t\ts.running.Store(false)\n\t\t}()\n\t\terr = s.Server.Serve()\n\t\tif err != nil {\n\t\t\tlogrus.WithField(\"addr\", addr).Errorf(\"serve error: %s\", err)\n\t\t}\n\t}()\n\treturn nil\n}\n\nfunc (s *Server) authByUser(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) {\n\tif _, r := s.l.CheckLimit(string(auth), addr.String(), false); r {\n\t\treturn false, \"device limited\"\n\t}\n\tif _, ok := s.users.Load(string(auth)); ok {\n\t\treturn true, \"Done\"\n\t}\n\treturn false, \"Failed\"\n}\n\nfunc (s *Server) connectFunc(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) {\n\ts.l.ConnLimiter.AddConnCount(addr.String(), string(auth), false)\n\tok, msg := s.authByUser(addr, auth, sSend, sRecv)\n\tif !ok {\n\t\tlogrus.WithFields(logrus.Fields{\n\t\t\t\"src\": defaultIPMasker.Mask(addr.String()),\n\t\t}).Info(\"Authentication failed, client rejected\")\n\t\treturn false, msg\n\t}\n\tlogrus.WithFields(logrus.Fields{\n\t\t\"src\":  defaultIPMasker.Mask(addr.String()),\n\t\t\"Uuid\": string(auth),\n\t\t\"Tag\":  s.tag,\n\t}).Info(\"Client connected\")\n\treturn ok, msg\n}\n\nfunc (s *Server) disconnectFunc(addr net.Addr, auth []byte, err error) {\n\ts.l.ConnLimiter.DelConnCount(addr.String(), string(auth))\n\tlogrus.WithFields(logrus.Fields{\n\t\t\"src\":   defaultIPMasker.Mask(addr.String()),\n\t\t\"error\": err,\n\t}).Info(\"Client disconnected\")\n}\n\nfunc tcpRequestFunc(addr net.Addr, auth []byte, reqAddr string, action acl.Action, arg string) {\n\tlogrus.WithFields(logrus.Fields{\n\t\t\"src\":    defaultIPMasker.Mask(addr.String()),\n\t\t\"dst\":    defaultIPMasker.Mask(reqAddr),\n\t\t\"action\": actionToString(action, arg),\n\t}).Debug(\"TCP request\")\n}\n\nfunc tcpErrorFunc(addr net.Addr, auth []byte, reqAddr string, err error) {\n\tif err != io.EOF {\n\t\tlogrus.WithFields(logrus.Fields{\n\t\t\t\"src\":   defaultIPMasker.Mask(addr.String()),\n\t\t\t\"dst\":   defaultIPMasker.Mask(reqAddr),\n\t\t\t\"error\": err,\n\t\t}).Info(\"TCP error\")\n\t} else {\n\t\tlogrus.WithFields(logrus.Fields{\n\t\t\t\"src\": defaultIPMasker.Mask(addr.String()),\n\t\t\t\"dst\": defaultIPMasker.Mask(reqAddr),\n\t\t}).Debug(\"TCP EOF\")\n\t}\n}\n\nfunc udpRequestFunc(addr net.Addr, auth []byte, sessionID uint32) {\n\tlogrus.WithFields(logrus.Fields{\n\t\t\"src\":     defaultIPMasker.Mask(addr.String()),\n\t\t\"session\": sessionID,\n\t}).Debug(\"UDP request\")\n}\n\nfunc udpErrorFunc(addr net.Addr, auth []byte, sessionID uint32, err error) {\n\tif err != io.EOF {\n\t\tlogrus.WithFields(logrus.Fields{\n\t\t\t\"src\":     defaultIPMasker.Mask(addr.String()),\n\t\t\t\"session\": sessionID,\n\t\t\t\"error\":   err,\n\t\t}).Info(\"UDP error\")\n\t} else {\n\t\tlogrus.WithFields(logrus.Fields{\n\t\t\t\"src\":     defaultIPMasker.Mask(addr.String()),\n\t\t\t\"session\": sessionID,\n\t\t}).Debug(\"UDP EOF\")\n\t}\n}\n\nfunc actionToString(action acl.Action, arg string) string {\n\tswitch action {\n\tcase acl.ActionDirect:\n\t\treturn \"Direct\"\n\tcase acl.ActionProxy:\n\t\treturn \"Proxy\"\n\tcase acl.ActionBlock:\n\t\treturn \"Block\"\n\tcase acl.ActionHijack:\n\t\treturn \"Hijack to \" + arg\n\tdefault:\n\t\treturn \"Unknown\"\n\t}\n}\n"
  },
  {
    "path": "core/hy/server_test.go",
    "content": "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\"github.com/Yuzuki616/V2bX/limiter\"\n\t\"github.com/sirupsen/logrus\"\n\t\"log\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestServer(t *testing.T) {\n\tlogrus.SetLevel(logrus.DebugLevel)\n\tlimiter.Init()\n\tl := limiter.AddLimiter(\"test\", &conf.LimitConfig{}, nil)\n\ts := NewServer(\"test\", l)\n\tt.Log(s.runServer(&panel.NodeInfo{\n\t\tPort:     1145,\n\t\tUpMbps:   100,\n\t\tDownMbps: 100,\n\t\tHyObfs:   \"atresssdaaaadd\",\n\t}, &conf.ControllerConfig{\n\t\tListenIP:  \"127.0.0.1\",\n\t\tHyOptions: conf.HyOptions{},\n\t\tCertConfig: &conf.CertConfig{\n\t\t\tCertFile: \"../../test_data/1.pem\",\n\t\t\tKeyFile:  \"../../test_data/1.key\",\n\t\t},\n\t}))\n\ts.users.Store(\"test1111\", struct{}{})\n\tgo func() {\n\t\tfor {\n\t\t\ttime.Sleep(10 * time.Second)\n\t\t\tauth := base64.StdEncoding.EncodeToString([]byte(\"test1111\"))\n\t\t\tlog.Println(auth)\n\t\t\tlog.Println(s.counter.getCounters(auth).UpCounter.Load())\n\t\t}\n\t}()\n\tselect {}\n}\n"
  },
  {
    "path": "core/hy/user.go",
    "content": "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/core\"\n)\n\nfunc (h *Hy) AddUsers(p *core.AddUsersParams) (int, error) {\n\ts, ok := h.servers.Load(p.Tag)\n\tif !ok {\n\t\treturn 0, errors.New(\"the node not have\")\n\t}\n\tu := &s.(*Server).users\n\tfor i := range p.UserInfo {\n\t\tu.Store(p.UserInfo[i].Uuid, struct{}{})\n\t}\n\treturn len(p.UserInfo), nil\n}\n\nfunc (h *Hy) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) {\n\tv, _ := h.servers.Load(tag)\n\ts := v.(*Server)\n\tauth := base64.StdEncoding.EncodeToString([]byte(uuid))\n\tup = s.counter.getCounters(auth).UpCounter.Load()\n\tdown = s.counter.getCounters(auth).DownCounter.Load()\n\tif reset {\n\t\ts.counter.Reset(auth)\n\t}\n\treturn\n}\n\nfunc (h *Hy) DelUsers(users []panel.UserInfo, tag string) error {\n\tv, e := h.servers.Load(tag)\n\tif !e {\n\t\treturn errors.New(\"the node is not have\")\n\t}\n\ts := v.(*Server)\n\tfor i := range users {\n\t\ts.users.Delete(users[i].Uuid)\n\t\ts.counter.Delete(base64.StdEncoding.EncodeToString([]byte(users[i].Uuid)))\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/imports/hy.go",
    "content": "//go:build hy\n\npackage imports\n\n// not yet tested\nimport _ \"github.com/Yuzuki616/V2bX/core/hy\"\n"
  },
  {
    "path": "core/imports/imports.go",
    "content": "package imports\n"
  },
  {
    "path": "core/imports/xray.go",
    "content": "//go:build xray\n\npackage imports\n\nimport _ \"github.com/Yuzuki616/V2bX/core/xray\"\n"
  },
  {
    "path": "core/interface.go",
    "content": "package core\n\nimport (\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/conf\"\n)\n\ntype AddUsersParams struct {\n\tTag      string\n\tConfig   *conf.ControllerConfig\n\tUserInfo []panel.UserInfo\n\tNodeInfo *panel.NodeInfo\n}\ntype Core interface {\n\tStart() error\n\tClose() error\n\tAddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error\n\tDelNode(tag string) error\n\tAddUsers(p *AddUsersParams) (added int, err error)\n\tGetUserTraffic(tag, uuid string, reset bool) (up int64, down int64)\n\tDelUsers(users []panel.UserInfo, tag string) error\n\tProtocols() []string\n}\n"
  },
  {
    "path": "core/selector.go",
    "content": "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\"github.com/hashicorp/go-multierror\"\n)\n\ntype Selector struct {\n\tcores []Core\n\tnodes sync.Map\n}\n\nfunc (s *Selector) Start() error {\n\tfor i := range s.cores {\n\t\terr := s.cores[i].Start()\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (s *Selector) Close() error {\n\tvar errs error\n\tfor i := range s.cores {\n\t\terrs = multierror.Append(errs, s.cores[i].Close())\n\t}\n\treturn errs\n}\n\nfunc isSupported(protocol string, protocols []string) bool {\n\tfor i := range protocols {\n\t\tif protocol == protocols[i] {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (s *Selector) AddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error {\n\tfor i := range s.cores {\n\t\tif !isSupported(info.Type, s.cores[i].Protocols()) {\n\t\t\tcontinue\n\t\t}\n\t\terr := s.cores[i].AddNode(tag, info, config)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ts.nodes.Store(tag, i)\n\t\treturn nil\n\t}\n\treturn errors.New(\"the node type is not support\")\n}\n\nfunc (s *Selector) DelNode(tag string) error {\n\tif t, e := s.nodes.Load(tag); e {\n\t\terr := s.cores[t.(int)].DelNode(tag)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ts.nodes.Delete(tag)\n\t\treturn nil\n\t}\n\treturn errors.New(\"the node is not have\")\n}\n\nfunc (s *Selector) AddUsers(p *AddUsersParams) (added int, err error) {\n\tt, e := s.nodes.Load(p.Tag)\n\tif !e {\n\t\treturn 0, errors.New(\"the node is not have\")\n\t}\n\treturn s.cores[t.(int)].AddUsers(p)\n}\n\nfunc (s *Selector) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) {\n\tt, e := s.nodes.Load(tag)\n\tif !e {\n\t\treturn 0, 0\n\t}\n\treturn s.cores[t.(int)].GetUserTraffic(tag, uuid, reset)\n}\n\nfunc (s *Selector) DelUsers(users []panel.UserInfo, tag string) error {\n\tt, e := s.nodes.Load(tag)\n\tif !e {\n\t\treturn errors.New(\"the node is not have\")\n\t}\n\treturn s.cores[t.(int)].DelUsers(users, tag)\n}\n\nfunc (s *Selector) Protocols() []string {\n\tprotocols := make([]string, 0)\n\tfor i := range s.cores {\n\t\tprotocols = append(protocols, s.cores[i].Protocols()...)\n\t}\n\treturn protocols\n}\n"
  },
  {
    "path": "core/xray/app/app.go",
    "content": "// 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",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.28.1\n// \tprotoc        v3.21.12\n// source: config.proto\n\npackage dispatcher\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype SessionConfig struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n}\n\nfunc (x *SessionConfig) Reset() {\n\t*x = SessionConfig{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_config_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *SessionConfig) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*SessionConfig) ProtoMessage() {}\n\nfunc (x *SessionConfig) ProtoReflect() protoreflect.Message {\n\tmi := &file_config_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use SessionConfig.ProtoReflect.Descriptor instead.\nfunc (*SessionConfig) Descriptor() ([]byte, []int) {\n\treturn file_config_proto_rawDescGZIP(), []int{0}\n}\n\ntype Config struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tSettings *SessionConfig `protobuf:\"bytes,1,opt,name=settings,proto3\" json:\"settings,omitempty\"`\n}\n\nfunc (x *Config) Reset() {\n\t*x = Config{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_config_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Config) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Config) ProtoMessage() {}\n\nfunc (x *Config) ProtoReflect() protoreflect.Message {\n\tmi := &file_config_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Config.ProtoReflect.Descriptor instead.\nfunc (*Config) Descriptor() ([]byte, []int) {\n\treturn file_config_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *Config) GetSettings() *SessionConfig {\n\tif x != nil {\n\t\treturn x.Settings\n\t}\n\treturn nil\n}\n\nvar File_config_proto protoreflect.FileDescriptor\n\nvar file_config_proto_rawDesc = []byte{\n\t0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x18,\n\t0x76, 0x32, 0x62, 0x78, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x69,\n\t0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x22, 0x15, 0x0a, 0x0d, 0x53, 0x65, 0x73, 0x73,\n\t0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22,\n\t0x4d, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x43, 0x0a, 0x08, 0x73, 0x65, 0x74,\n\t0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x76, 0x32,\n\t0x62, 0x78, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x69, 0x73, 0x70,\n\t0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f,\n\t0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x6a,\n\t0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x62, 0x78, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,\n\t0x61, 0x70, 0x70, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x50, 0x01,\n\t0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x59, 0x75, 0x7a,\n\t0x75, 0x6b, 0x69, 0x36, 0x31, 0x36, 0x2f, 0x56, 0x32, 0x62, 0x58, 0x2f, 0x63, 0x6f, 0x72, 0x65,\n\t0x2f, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0xaa,\n\t0x02, 0x18, 0x56, 0x32, 0x62, 0x58, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e,\n\t0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x33,\n}\n\nvar (\n\tfile_config_proto_rawDescOnce sync.Once\n\tfile_config_proto_rawDescData = file_config_proto_rawDesc\n)\n\nfunc file_config_proto_rawDescGZIP() []byte {\n\tfile_config_proto_rawDescOnce.Do(func() {\n\t\tfile_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_config_proto_rawDescData)\n\t})\n\treturn file_config_proto_rawDescData\n}\n\nvar file_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)\nvar file_config_proto_goTypes = []interface{}{\n\t(*SessionConfig)(nil), // 0: v2bx.core.app.dispatcher.SessionConfig\n\t(*Config)(nil),        // 1: v2bx.core.app.dispatcher.Config\n}\nvar file_config_proto_depIdxs = []int32{\n\t0, // 0: v2bx.core.app.dispatcher.Config.settings:type_name -> v2bx.core.app.dispatcher.SessionConfig\n\t1, // [1:1] is the sub-list for method output_type\n\t1, // [1:1] is the sub-list for method input_type\n\t1, // [1:1] is the sub-list for extension type_name\n\t1, // [1:1] is the sub-list for extension extendee\n\t0, // [0:1] is the sub-list for field type_name\n}\n\nfunc init() { file_config_proto_init() }\nfunc file_config_proto_init() {\n\tif File_config_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*SessionConfig); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Config); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_config_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   2,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_config_proto_goTypes,\n\t\tDependencyIndexes: file_config_proto_depIdxs,\n\t\tMessageInfos:      file_config_proto_msgTypes,\n\t}.Build()\n\tFile_config_proto = out.File\n\tfile_config_proto_rawDesc = nil\n\tfile_config_proto_goTypes = nil\n\tfile_config_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "core/xray/app/dispatcher/config.proto",
    "content": "syntax = \"proto3\";\n\npackage v2bx.core.app.dispatcher;\noption csharp_namespace = \"V2bX.core.app.dispatcher\";\noption go_package = \"github.com/Yuzuki616/V2bX/core/xray/app/dispatcher\";\noption java_package = \"com.v2bx.core.app.dispatcher\";\noption java_multiple_files = true;\n\nmessage SessionConfig {\n  reserved 1;\n}\n\nmessage Config {\n  SessionConfig settings = 1;\n}\n"
  },
  {
    "path": "core/xray/app/dispatcher/default.go",
    "content": "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\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/Yuzuki616/V2bX/common/rate\"\n\t\"github.com/Yuzuki616/V2bX/limiter\"\n\troutingSession \"github.com/xtls/xray-core/features/routing/session\"\n\n\t\"github.com/xtls/xray-core/common\"\n\t\"github.com/xtls/xray-core/common/buf\"\n\t\"github.com/xtls/xray-core/common/log\"\n\t\"github.com/xtls/xray-core/common/net\"\n\t\"github.com/xtls/xray-core/common/protocol\"\n\t\"github.com/xtls/xray-core/common/session\"\n\t\"github.com/xtls/xray-core/core\"\n\t\"github.com/xtls/xray-core/features/dns\"\n\t\"github.com/xtls/xray-core/features/outbound\"\n\t\"github.com/xtls/xray-core/features/policy\"\n\t\"github.com/xtls/xray-core/features/routing\"\n\t\"github.com/xtls/xray-core/features/stats\"\n\t\"github.com/xtls/xray-core/transport\"\n\t\"github.com/xtls/xray-core/transport/pipe\"\n)\n\nvar errSniffingTimeout = newError(\"timeout on sniffing\")\n\ntype cachedReader struct {\n\tsync.Mutex\n\treader *pipe.Reader\n\tcache  buf.MultiBuffer\n}\n\nfunc (r *cachedReader) Cache(b *buf.Buffer) {\n\tmb, _ := r.reader.ReadMultiBufferTimeout(time.Millisecond * 100)\n\tr.Lock()\n\tif !mb.IsEmpty() {\n\t\tr.cache, _ = buf.MergeMulti(r.cache, mb)\n\t}\n\tb.Clear()\n\trawBytes := b.Extend(buf.Size)\n\tn := r.cache.Copy(rawBytes)\n\tb.Resize(0, int32(n))\n\tr.Unlock()\n}\n\nfunc (r *cachedReader) readInternal() buf.MultiBuffer {\n\tr.Lock()\n\tdefer r.Unlock()\n\n\tif r.cache != nil && !r.cache.IsEmpty() {\n\t\tmb := r.cache\n\t\tr.cache = nil\n\t\treturn mb\n\t}\n\n\treturn nil\n}\n\nfunc (r *cachedReader) ReadMultiBuffer() (buf.MultiBuffer, error) {\n\tmb := r.readInternal()\n\tif mb != nil {\n\t\treturn mb, nil\n\t}\n\n\treturn r.reader.ReadMultiBuffer()\n}\n\nfunc (r *cachedReader) ReadMultiBufferTimeout(timeout time.Duration) (buf.MultiBuffer, error) {\n\tmb := r.readInternal()\n\tif mb != nil {\n\t\treturn mb, nil\n\t}\n\n\treturn r.reader.ReadMultiBufferTimeout(timeout)\n}\n\nfunc (r *cachedReader) Interrupt() {\n\tr.Lock()\n\tif r.cache != nil {\n\t\tr.cache = buf.ReleaseMulti(r.cache)\n\t}\n\tr.Unlock()\n\tr.reader.Interrupt()\n}\n\n// DefaultDispatcher is a default implementation of Dispatcher.\ntype DefaultDispatcher struct {\n\tohm    outbound.Manager\n\trouter routing.Router\n\tpolicy policy.Manager\n\tstats  stats.Manager\n\tdns    dns.Client\n\tfdns   dns.FakeDNSEngine\n}\n\nfunc init() {\n\tcommon.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {\n\t\td := new(DefaultDispatcher)\n\t\tif err := core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dc dns.Client) error {\n\t\t\tcore.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {\n\t\t\t\td.fdns = fdns\n\t\t\t})\n\t\t\treturn d.Init(config.(*Config), om, router, pm, sm, dc)\n\t\t}); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn d, nil\n\t}))\n}\n\n// Init initializes DefaultDispatcher.\nfunc (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dns dns.Client) error {\n\td.ohm = om\n\td.router = router\n\td.policy = pm\n\td.stats = sm\n\td.dns = dns\n\treturn nil\n}\n\n// Type implements common.HasType.\nfunc (*DefaultDispatcher) Type() interface{} {\n\treturn routing.DispatcherType()\n}\n\n// Start implements common.Runnable.\nfunc (*DefaultDispatcher) Start() error {\n\treturn nil\n}\n\n// Close implements common.Closable.\nfunc (*DefaultDispatcher) Close() error { return nil }\n\nfunc (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sniffing session.SniffingRequest) (*transport.Link, *transport.Link, *limiter.Limiter, error) {\n\tdownOpt := pipe.OptionsFromContext(ctx)\n\tupOpt := downOpt\n\n\tif network == net.Network_UDP {\n\t\tvar ip2domain *sync.Map // net.IP.String() => domain, this map is used by server side when client turn on fakedns\n\t\t// Client will send domain address in the buffer.UDP.Address, server record all possible target IP addrs.\n\t\t// When target replies, server will restore the domain and send back to client.\n\t\t// Note: this map is not global but per connection context\n\t\tupOpt = append(upOpt, pipe.OnTransmission(func(mb buf.MultiBuffer) buf.MultiBuffer {\n\t\t\tfor i, buffer := range mb {\n\t\t\t\tif buffer.UDP == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\taddr := buffer.UDP.Address\n\t\t\t\tif addr.Family().IsIP() {\n\t\t\t\t\tif fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && fkr0.IsIPInIPPool(addr) && sniffing.Enabled {\n\t\t\t\t\t\tdomain := fkr0.GetDomainFromFakeDNS(addr)\n\t\t\t\t\t\tif len(domain) > 0 {\n\t\t\t\t\t\t\tbuffer.UDP.Address = net.DomainAddress(domain)\n\t\t\t\t\t\t\tnewError(\"[fakedns client] override with domain: \", domain, \" for xUDP buffer at \", i).WriteToLog(session.ExportIDToError(ctx))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnewError(\"[fakedns client] failed to find domain! :\", addr.String(), \" for xUDP buffer at \", i).AtWarning().WriteToLog(session.ExportIDToError(ctx))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif ip2domain == nil {\n\t\t\t\t\t\tip2domain = new(sync.Map)\n\t\t\t\t\t\tnewError(\"[fakedns client] create a new map\").WriteToLog(session.ExportIDToError(ctx))\n\t\t\t\t\t}\n\t\t\t\t\tdomain := addr.Domain()\n\t\t\t\t\tips, err := d.dns.LookupIP(domain, dns.IPOption{true, true, false})\n\t\t\t\t\tif err == nil {\n\t\t\t\t\t\tfor _, ip := range ips {\n\t\t\t\t\t\t\tip2domain.Store(ip.String(), domain)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnewError(\"[fakedns client] candidate ip: \"+fmt.Sprintf(\"%v\", ips), \" for xUDP buffer at \", i).WriteToLog(session.ExportIDToError(ctx))\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnewError(\"[fakedns client] failed to look up IP for \", domain, \" for xUDP buffer at \", i).Base(err).WriteToLog(session.ExportIDToError(ctx))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn mb\n\t\t}))\n\t\tdownOpt = append(downOpt, pipe.OnTransmission(func(mb buf.MultiBuffer) buf.MultiBuffer {\n\t\t\tfor i, buffer := range mb {\n\t\t\t\tif buffer.UDP == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\taddr := buffer.UDP.Address\n\t\t\t\tif addr.Family().IsIP() {\n\t\t\t\t\tif ip2domain == nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif domain, found := ip2domain.Load(addr.IP().String()); found {\n\t\t\t\t\t\tbuffer.UDP.Address = net.DomainAddress(domain.(string))\n\t\t\t\t\t\tnewError(\"[fakedns client] restore domain: \", domain.(string), \" for xUDP buffer at \", i).WriteToLog(session.ExportIDToError(ctx))\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok {\n\t\t\t\t\t\tfakeIp := fkr0.GetFakeIPForDomain(addr.Domain())\n\t\t\t\t\t\tbuffer.UDP.Address = fakeIp[0]\n\t\t\t\t\t\tnewError(\"[fakedns client] restore FakeIP: \", buffer.UDP, fmt.Sprintf(\"%v\", fakeIp), \" for xUDP buffer at \", i).WriteToLog(session.ExportIDToError(ctx))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn mb\n\t\t}))\n\t}\n\tuplinkReader, uplinkWriter := pipe.New(upOpt...)\n\tdownlinkReader, downlinkWriter := pipe.New(downOpt...)\n\n\tinboundLink := &transport.Link{\n\t\tReader: downlinkReader,\n\t\tWriter: uplinkWriter,\n\t}\n\n\toutboundLink := &transport.Link{\n\t\tReader: uplinkReader,\n\t\tWriter: downlinkWriter,\n\t}\n\n\tsessionInbound := session.InboundFromContext(ctx)\n\tvar user *protocol.MemoryUser\n\tif sessionInbound != nil {\n\t\tuser = sessionInbound.User\n\t}\n\tvar limit *limiter.Limiter\n\tif user != nil && len(user.Email) > 0 {\n\t\tvar err error\n\t\tlimit, err = limiter.GetLimiter(sessionInbound.Tag)\n\t\tif err != nil {\n\t\t\tnewError(\"Get limit info error: \", err).AtError().WriteToLog()\n\t\t\tcommon.Close(outboundLink.Writer)\n\t\t\tcommon.Close(inboundLink.Writer)\n\t\t\tcommon.Interrupt(outboundLink.Reader)\n\t\t\tcommon.Interrupt(inboundLink.Reader)\n\t\t\treturn nil, nil, nil, newError(\"Get limit info error: \", err)\n\t\t}\n\t\t// Speed Limit and Device Limit\n\t\tw, reject := limit.CheckLimit(user.Email,\n\t\t\tsessionInbound.Source.Address.IP().String(),\n\t\t\tnetwork == net.Network_TCP)\n\t\tif reject {\n\t\t\tnewError(\"Limited \", user.Email, \" by conn or ip\").AtWarning().WriteToLog()\n\t\t\tcommon.Close(outboundLink.Writer)\n\t\t\tcommon.Close(inboundLink.Writer)\n\t\t\tcommon.Interrupt(outboundLink.Reader)\n\t\t\tcommon.Interrupt(inboundLink.Reader)\n\t\t\treturn nil, nil, nil, newError(\"Limited \", user.Email, \" by conn or ip\")\n\t\t}\n\t\tif w != nil {\n\t\t\tinboundLink.Writer = rate.NewRateLimitWriter(inboundLink.Writer, w)\n\t\t\toutboundLink.Writer = rate.NewRateLimitWriter(outboundLink.Writer, w)\n\t\t}\n\t\tp := d.policy.ForLevel(user.Level)\n\t\tif p.Stats.UserUplink {\n\t\t\tname := \"user>>>\" + user.Email + \">>>traffic>>>uplink\"\n\t\t\tif c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {\n\t\t\t\tinboundLink.Writer = &SizeStatWriter{\n\t\t\t\t\tCounter: c,\n\t\t\t\t\tWriter:  inboundLink.Writer,\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif p.Stats.UserDownlink {\n\t\t\tname := \"user>>>\" + user.Email + \">>>traffic>>>downlink\"\n\t\t\tif c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {\n\t\t\t\toutboundLink.Writer = &SizeStatWriter{\n\t\t\t\t\tCounter: c,\n\t\t\t\t\tWriter:  outboundLink.Writer,\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn inboundLink, outboundLink, limit, nil\n}\n\nfunc (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool {\n\tdomain := result.Domain()\n\tif domain == \"\" {\n\t\treturn false\n\t}\n\tfor _, d := range request.ExcludeForDomain {\n\t\tif strings.ToLower(domain) == d {\n\t\t\treturn false\n\t\t}\n\t}\n\tprotocolString := result.Protocol()\n\tif resComp, ok := result.(SnifferResultComposite); ok {\n\t\tprotocolString = resComp.ProtocolForDomainResult()\n\t}\n\tfor _, p := range request.OverrideDestinationForProtocol {\n\t\tif strings.HasPrefix(protocolString, p) {\n\t\t\treturn true\n\t\t}\n\t\tif fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && protocolString != \"bittorrent\" && p == \"fakedns\" &&\n\t\t\tdestination.Address.Family().IsIP() && fkr0.IsIPInIPPool(destination.Address) {\n\t\t\tnewError(\"Using sniffer \", protocolString, \" since the fake DNS missed\").WriteToLog(session.ExportIDToError(ctx))\n\t\t\treturn true\n\t\t}\n\t\tif resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok {\n\t\t\tif resultSubset.IsProtoSubsetOf(p) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false\n}\n\n// Dispatch implements routing.Dispatcher.\nfunc (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (*transport.Link, error) {\n\tif !destination.IsValid() {\n\t\tpanic(\"Dispatcher: Invalid destination.\")\n\t}\n\tob := &session.Outbound{\n\t\tTarget: destination,\n\t}\n\tctx = session.ContextWithOutbound(ctx, ob)\n\tcontent := session.ContentFromContext(ctx)\n\tif content == nil {\n\t\tcontent = new(session.Content)\n\t\tctx = session.ContextWithContent(ctx, content)\n\t}\n\tsniffingRequest := content.SniffingRequest\n\tinbound, outbound, l, err := d.getLink(ctx, destination.Network, sniffingRequest)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !sniffingRequest.Enabled {\n\t\tgo d.routedDispatch(ctx, outbound, destination, l)\n\t} else {\n\t\tgo func() {\n\t\t\tcReader := &cachedReader{\n\t\t\t\treader: outbound.Reader.(*pipe.Reader),\n\t\t\t}\n\t\t\toutbound.Reader = cReader\n\t\t\tresult, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network, l)\n\t\t\tif _, ok := err.(limitedError); ok {\n\t\t\t\tnewError(err).AtInfo().WriteToLog()\n\t\t\t\tcommon.Close(outbound.Writer)\n\t\t\t\tcommon.Interrupt(outbound.Reader)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err == nil {\n\t\t\t\tcontent.Protocol = result.Protocol()\n\t\t\t}\n\t\t\tif err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {\n\t\t\t\tdomain := result.Domain()\n\t\t\t\tnewError(\"sniffed domain: \", domain).WriteToLog(session.ExportIDToError(ctx))\n\t\t\t\tdestination.Address = net.ParseAddress(domain)\n\t\t\t\tif sniffingRequest.RouteOnly && result.Protocol() != \"fakedns\" {\n\t\t\t\t\tob.RouteTarget = destination\n\t\t\t\t} else {\n\t\t\t\t\tob.Target = destination\n\t\t\t\t}\n\t\t\t}\n\t\t\td.routedDispatch(ctx, outbound, destination, l)\n\t\t}()\n\t}\n\treturn inbound, nil\n}\n\n// DispatchLink implements routing.Dispatcher.\nfunc (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.Destination, outbound *transport.Link) error {\n\tif !destination.IsValid() {\n\t\treturn newError(\"Dispatcher: Invalid destination.\")\n\t}\n\tob := &session.Outbound{\n\t\tTarget: destination,\n\t}\n\tctx = session.ContextWithOutbound(ctx, ob)\n\tcontent := session.ContentFromContext(ctx)\n\tif content == nil {\n\t\tcontent = new(session.Content)\n\t\tctx = session.ContextWithContent(ctx, content)\n\t}\n\tsniffingRequest := content.SniffingRequest\n\tif !sniffingRequest.Enabled {\n\t\tgo d.routedDispatch(ctx, outbound, destination, nil)\n\t} else {\n\t\tgo func() {\n\t\t\tcReader := &cachedReader{\n\t\t\t\treader: outbound.Reader.(*pipe.Reader),\n\t\t\t}\n\t\t\toutbound.Reader = cReader\n\t\t\tresult, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network, nil)\n\t\t\tif _, ok := err.(limitedError); ok {\n\t\t\t\tnewError(err).AtInfo().WriteToLog()\n\t\t\t\tcommon.Close(outbound.Writer)\n\t\t\t\tcommon.Interrupt(outbound.Reader)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err == nil {\n\t\t\t\tcontent.Protocol = result.Protocol()\n\t\t\t}\n\t\t\tif err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {\n\t\t\t\tdomain := result.Domain()\n\t\t\t\tnewError(\"sniffed domain: \", domain).WriteToLog(session.ExportIDToError(ctx))\n\t\t\t\tdestination.Address = net.ParseAddress(domain)\n\t\t\t\tif sniffingRequest.RouteOnly && result.Protocol() != \"fakedns\" {\n\t\t\t\t\tob.RouteTarget = destination\n\t\t\t\t} else {\n\t\t\t\t\tob.Target = destination\n\t\t\t\t}\n\t\t\t}\n\t\t\td.routedDispatch(ctx, outbound, destination, nil)\n\t\t}()\n\t}\n\treturn nil\n}\n\ntype limitedError string\n\nfunc (l limitedError) Error() string {\n\treturn string(l)\n}\n\nfunc sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network, l *limiter.Limiter) (result SniffResult, err error) {\n\tpayload := buf.New()\n\tdefer payload.Release()\n\n\tdefer func() {\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t// Check if domain and protocol hit the rule\n\t\tsessionInbound := session.InboundFromContext(ctx)\n\t\t// Whether the inbound connection contains a user\n\t\tif sessionInbound.User != nil {\n\t\t\tif l == nil {\n\t\t\t\tl, err = limiter.GetLimiter(sessionInbound.Tag)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tif l.CheckDomainRule(result.Domain()) {\n\t\t\t\terr = limitedError(fmt.Sprintf(\n\t\t\t\t\t\"User %s access domain %s reject by rule\",\n\t\t\t\t\tsessionInbound.User.Email,\n\t\t\t\t\tresult.Domain()))\n\t\t\t}\n\t\t\tif l.CheckProtocolRule(result.Protocol()) {\n\t\t\t\terr = limitedError(fmt.Sprintf(\n\t\t\t\t\t\"User %s access protocol %s reject by rule\",\n\t\t\t\t\tsessionInbound.User.Email,\n\t\t\t\t\tresult.Protocol()))\n\t\t\t}\n\t\t}\n\t}()\n\n\tsniffer := NewSniffer(ctx)\n\tmetaresult, metadataErr := sniffer.SniffMetadata(ctx)\n\tif metadataOnly {\n\t\treturn metaresult, metadataErr\n\t}\n\tcontentResult, contentErr := func() (SniffResult, error) {\n\t\ttotalAttempt := 0\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn nil, ctx.Err()\n\t\t\tdefault:\n\t\t\t\ttotalAttempt++\n\t\t\t\tif totalAttempt > 2 {\n\t\t\t\t\treturn nil, errSniffingTimeout\n\t\t\t\t}\n\n\t\t\t\tcReader.Cache(payload)\n\t\t\t\tif !payload.IsEmpty() {\n\t\t\t\t\tresult, err := sniffer.Sniff(ctx, payload.Bytes(), network)\n\t\t\t\t\tif err != common.ErrNoClue {\n\t\t\t\t\t\treturn result, err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif payload.IsFull() {\n\t\t\t\t\treturn nil, errUnknownContent\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\tif contentErr != nil && metadataErr == nil {\n\t\treturn metaresult, nil\n\t}\n\tif contentErr == nil && metadataErr == nil {\n\t\treturn CompositeResult(metaresult, contentResult), nil\n\t}\n\treturn contentResult, contentErr\n}\n\nfunc (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination, l *limiter.Limiter) {\n\tob := session.OutboundFromContext(ctx)\n\tif hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() {\n\t\tproxied := hosts.LookupHosts(ob.Target.String())\n\t\tif proxied != nil {\n\t\t\tro := ob.RouteTarget == destination\n\t\t\tdestination.Address = *proxied\n\t\t\tif ro {\n\t\t\t\tob.RouteTarget = destination\n\t\t\t} else {\n\t\t\t\tob.Target = destination\n\t\t\t}\n\t\t}\n\t}\n\tvar handler outbound.Handler\n\n\t// del connect count\n\tif l != nil {\n\t\tsessionInbound := session.InboundFromContext(ctx)\n\t\tif sessionInbound.User != nil {\n\t\t\tif destination.Network == net.Network_TCP {\n\t\t\t\tdefer func() {\n\t\t\t\t\tl.ConnLimiter.DelConnCount(sessionInbound.User.Email, sessionInbound.Source.Address.IP().String())\n\t\t\t\t}()\n\t\t\t}\n\t\t}\n\t}\n\n\troutingLink := routingSession.AsRoutingContext(ctx)\n\tinTag := routingLink.GetInboundTag()\n\tisPickRoute := 0\n\tif forcedOutboundTag := session.GetForcedOutboundTagFromContext(ctx); forcedOutboundTag != \"\" {\n\t\tctx = session.SetForcedOutboundTagToContext(ctx, \"\")\n\t\tif h := d.ohm.GetHandler(forcedOutboundTag); h != nil {\n\t\t\tisPickRoute = 1\n\t\t\tnewError(\"taking platform initialized detour [\", forcedOutboundTag, \"] for [\", destination, \"]\").WriteToLog(session.ExportIDToError(ctx))\n\t\t\thandler = h\n\t\t} else {\n\t\t\tnewError(\"non existing tag for platform initialized detour: \", forcedOutboundTag).AtError().WriteToLog(session.ExportIDToError(ctx))\n\t\t\tcommon.Close(link.Writer)\n\t\t\tcommon.Interrupt(link.Reader)\n\t\t\treturn\n\t\t}\n\t} else if d.router != nil {\n\t\tif route, err := d.router.PickRoute(routingLink); err == nil {\n\t\t\toutTag := route.GetOutboundTag()\n\t\t\tif h := d.ohm.GetHandler(outTag); h != nil {\n\t\t\t\tisPickRoute = 2\n\t\t\t\tnewError(\"taking detour [\", outTag, \"] for [\", destination, \"]\").WriteToLog(session.ExportIDToError(ctx))\n\t\t\t\thandler = h\n\t\t\t} else {\n\t\t\t\tnewError(\"non existing outTag: \", outTag).AtWarning().WriteToLog(session.ExportIDToError(ctx))\n\t\t\t}\n\t\t} else {\n\t\t\tnewError(\"default route for \", destination).WriteToLog(session.ExportIDToError(ctx))\n\t\t}\n\t}\n\n\tif handler == nil {\n\t\thandler = d.ohm.GetHandler(inTag) // Default outbound handier tag should be as same as the inbound tag\n\t}\n\n\t// If there is no outbound with tag as same as the inbound tag\n\tif handler == nil {\n\t\thandler = d.ohm.GetDefaultHandler()\n\t}\n\n\tif handler == nil {\n\t\tnewError(\"default outbound handler not exist\").WriteToLog(session.ExportIDToError(ctx))\n\t\tcommon.Close(link.Writer)\n\t\tcommon.Interrupt(link.Reader)\n\t\treturn\n\t}\n\n\tif accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil {\n\t\tif tag := handler.Tag(); tag != \"\" {\n\t\t\tif inTag == \"\" {\n\t\t\t\taccessMessage.Detour = tag\n\t\t\t} else if isPickRoute == 1 {\n\t\t\t\taccessMessage.Detour = inTag + \" ==> \" + tag\n\t\t\t} else if isPickRoute == 2 {\n\t\t\t\taccessMessage.Detour = inTag + \" -> \" + tag\n\t\t\t} else {\n\t\t\t\taccessMessage.Detour = inTag + \" >> \" + tag\n\t\t\t}\n\t\t}\n\t\tlog.Record(accessMessage)\n\t}\n\n\thandler.Dispatch(ctx, link)\n}\n"
  },
  {
    "path": "core/xray/app/dispatcher/dispatcher.go",
    "content": "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",
    "content": "package dispatcher\n\nimport \"github.com/xtls/xray-core/common/errors\"\n\ntype errPathObjHolder struct{}\n\nfunc newError(values ...interface{}) *errors.Error {\n\treturn errors.New(values...).WithPathObj(errPathObjHolder{})\n}\n"
  },
  {
    "path": "core/xray/app/dispatcher/fakednssniffer.go",
    "content": "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/common/net\"\n\t\"github.com/xtls/xray-core/common/session\"\n\t\"github.com/xtls/xray-core/core\"\n\t\"github.com/xtls/xray-core/features/dns\"\n)\n\n// newFakeDNSSniffer Creates a Fake DNS metadata sniffer\nfunc newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error) {\n\tvar fakeDNSEngine dns.FakeDNSEngine\n\t{\n\t\tfakeDNSEngineFeat := core.MustFromContext(ctx).GetFeature((*dns.FakeDNSEngine)(nil))\n\t\tif fakeDNSEngineFeat != nil {\n\t\t\tfakeDNSEngine = fakeDNSEngineFeat.(dns.FakeDNSEngine)\n\t\t}\n\t}\n\n\tif fakeDNSEngine == nil {\n\t\terrNotInit := newError(\"FakeDNSEngine is not initialized, but such a sniffer is used\").AtError()\n\t\treturn protocolSnifferWithMetadata{}, errNotInit\n\t}\n\treturn protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {\n\t\tTarget := session.OutboundFromContext(ctx).Target\n\t\tif Target.Network == net.Network_TCP || Target.Network == net.Network_UDP {\n\t\t\tdomainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(Target.Address)\n\t\t\tif domainFromFakeDNS != \"\" {\n\t\t\t\tnewError(\"fake dns got domain: \", domainFromFakeDNS, \" for ip: \", Target.Address.String()).WriteToLog(session.ExportIDToError(ctx))\n\t\t\t\treturn &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil\n\t\t\t}\n\t\t}\n\n\t\tif ipAddressInRangeValueI := ctx.Value(ipAddressInRange); ipAddressInRangeValueI != nil {\n\t\t\tipAddressInRangeValue := ipAddressInRangeValueI.(*ipAddressInRangeOpt)\n\t\t\tif fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok {\n\t\t\t\tinPool := fkr0.IsIPInIPPool(Target.Address)\n\t\t\t\tipAddressInRangeValue.addressInRange = &inPool\n\t\t\t}\n\t\t}\n\n\t\treturn nil, common.ErrNoClue\n\t}, metadataSniffer: true}, nil\n}\n\ntype fakeDNSSniffResult struct {\n\tdomainName string\n}\n\nfunc (fakeDNSSniffResult) Protocol() string {\n\treturn \"fakedns\"\n}\n\nfunc (f fakeDNSSniffResult) Domain() string {\n\treturn f.domainName\n}\n\ntype fakeDNSExtraOpts int\n\nconst ipAddressInRange fakeDNSExtraOpts = 1\n\ntype ipAddressInRangeOpt struct {\n\taddressInRange *bool\n}\n\ntype DNSThenOthersSniffResult struct {\n\tdomainName           string\n\tprotocolOriginalName string\n}\n\nfunc (f DNSThenOthersSniffResult) IsProtoSubsetOf(protocolName string) bool {\n\treturn strings.HasPrefix(protocolName, f.protocolOriginalName)\n}\n\nfunc (DNSThenOthersSniffResult) Protocol() string {\n\treturn \"fakedns+others\"\n}\n\nfunc (f DNSThenOthersSniffResult) Domain() string {\n\treturn f.domainName\n}\n\nfunc newFakeDNSThenOthers(ctx context.Context, fakeDNSSniffer protocolSnifferWithMetadata, others []protocolSnifferWithMetadata) (\n\tprotocolSnifferWithMetadata, error,\n) { // nolint: unparam\n\t// ctx may be used in the future\n\t_ = ctx\n\treturn protocolSnifferWithMetadata{\n\t\tprotocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {\n\t\t\tipAddressInRangeValue := &ipAddressInRangeOpt{}\n\t\t\tctx = context.WithValue(ctx, ipAddressInRange, ipAddressInRangeValue)\n\t\t\tresult, err := fakeDNSSniffer.protocolSniffer(ctx, bytes)\n\t\t\tif err == nil {\n\t\t\t\treturn result, nil\n\t\t\t}\n\t\t\tif ipAddressInRangeValue.addressInRange != nil {\n\t\t\t\tif *ipAddressInRangeValue.addressInRange {\n\t\t\t\t\tfor _, v := range others {\n\t\t\t\t\t\tif v.metadataSniffer || bytes != nil {\n\t\t\t\t\t\t\tif result, err := v.protocolSniffer(ctx, bytes); err == nil {\n\t\t\t\t\t\t\t\treturn DNSThenOthersSniffResult{domainName: result.Domain(), protocolOriginalName: result.Protocol()}, nil\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn nil, common.ErrNoClue\n\t\t\t\t}\n\t\t\t\tnewError(\"ip address not in fake dns range, return as is\").AtDebug().WriteToLog()\n\t\t\t\treturn nil, common.ErrNoClue\n\t\t\t}\n\t\t\tnewError(\"fake dns sniffer did not set address in range option, assume false.\").AtWarning().WriteToLog()\n\t\t\treturn nil, common.ErrNoClue\n\t\t},\n\t\tmetadataSniffer: false,\n\t}, nil\n}\n"
  },
  {
    "path": "core/xray/app/dispatcher/sniffer.go",
    "content": "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\"github.com/xtls/xray-core/common/protocol/bittorrent\"\n\t\"github.com/xtls/xray-core/common/protocol/http\"\n\t\"github.com/xtls/xray-core/common/protocol/quic\"\n\t\"github.com/xtls/xray-core/common/protocol/tls\"\n)\n\ntype SniffResult interface {\n\tProtocol() string\n\tDomain() string\n}\n\ntype protocolSniffer func(context.Context, []byte) (SniffResult, error)\n\ntype protocolSnifferWithMetadata struct {\n\tprotocolSniffer protocolSniffer\n\t// A Metadata sniffer will be invoked on connection establishment only, with nil body,\n\t// for both TCP and UDP connections\n\t// It will not be shown as a traffic type for routing unless there is no other successful sniffing.\n\tmetadataSniffer bool\n\tnetwork         net.Network\n}\n\ntype Sniffer struct {\n\tsniffer []protocolSnifferWithMetadata\n}\n\nfunc NewSniffer(ctx context.Context) *Sniffer {\n\tret := &Sniffer{\n\t\tsniffer: []protocolSnifferWithMetadata{\n\t\t\t{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false, net.Network_TCP},\n\t\t\t{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false, net.Network_TCP},\n\t\t\t{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false, net.Network_TCP},\n\t\t\t{func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP},\n\t\t\t{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffUTP(b) }, false, net.Network_UDP},\n\t\t},\n\t}\n\tif sniffer, err := newFakeDNSSniffer(ctx); err == nil {\n\t\tothers := ret.sniffer\n\t\tret.sniffer = append(ret.sniffer, sniffer)\n\t\tfakeDNSThenOthers, err := newFakeDNSThenOthers(ctx, sniffer, others)\n\t\tif err == nil {\n\t\t\tret.sniffer = append([]protocolSnifferWithMetadata{fakeDNSThenOthers}, ret.sniffer...)\n\t\t}\n\t}\n\treturn ret\n}\n\nvar errUnknownContent = newError(\"unknown content\")\n\nfunc (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) {\n\tvar pendingSniffer []protocolSnifferWithMetadata\n\tfor _, si := range s.sniffer {\n\t\ts := si.protocolSniffer\n\t\tif si.metadataSniffer || si.network != network {\n\t\t\tcontinue\n\t\t}\n\t\tresult, err := s(c, payload)\n\t\tif err == common.ErrNoClue {\n\t\t\tpendingSniffer = append(pendingSniffer, si)\n\t\t\tcontinue\n\t\t}\n\n\t\tif err == nil && result != nil {\n\t\t\treturn result, nil\n\t\t}\n\t}\n\n\tif len(pendingSniffer) > 0 {\n\t\ts.sniffer = pendingSniffer\n\t\treturn nil, common.ErrNoClue\n\t}\n\n\treturn nil, errUnknownContent\n}\n\nfunc (s *Sniffer) SniffMetadata(c context.Context) (SniffResult, error) {\n\tvar pendingSniffer []protocolSnifferWithMetadata\n\tfor _, si := range s.sniffer {\n\t\ts := si.protocolSniffer\n\t\tif !si.metadataSniffer {\n\t\t\tpendingSniffer = append(pendingSniffer, si)\n\t\t\tcontinue\n\t\t}\n\t\tresult, err := s(c, nil)\n\t\tif err == common.ErrNoClue {\n\t\t\tpendingSniffer = append(pendingSniffer, si)\n\t\t\tcontinue\n\t\t}\n\n\t\tif err == nil && result != nil {\n\t\t\treturn result, nil\n\t\t}\n\t}\n\n\tif len(pendingSniffer) > 0 {\n\t\ts.sniffer = pendingSniffer\n\t\treturn nil, common.ErrNoClue\n\t}\n\n\treturn nil, errUnknownContent\n}\n\nfunc CompositeResult(domainResult SniffResult, protocolResult SniffResult) SniffResult {\n\treturn &compositeResult{domainResult: domainResult, protocolResult: protocolResult}\n}\n\ntype compositeResult struct {\n\tdomainResult   SniffResult\n\tprotocolResult SniffResult\n}\n\nfunc (c compositeResult) Protocol() string {\n\treturn c.protocolResult.Protocol()\n}\n\nfunc (c compositeResult) Domain() string {\n\treturn c.domainResult.Domain()\n}\n\nfunc (c compositeResult) ProtocolForDomainResult() string {\n\treturn c.domainResult.Protocol()\n}\n\ntype SnifferResultComposite interface {\n\tProtocolForDomainResult() string\n}\n\ntype SnifferIsProtoSubsetOf interface {\n\tIsProtoSubsetOf(protocolName string) bool\n}\n"
  },
  {
    "path": "core/xray/app/dispatcher/stats.go",
    "content": "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/xtls/xray-core/features/stats\"\n)\n\ntype SizeStatWriter struct {\n\tCounter stats.Counter\n\tWriter  buf.Writer\n}\n\nfunc (w *SizeStatWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {\n\tw.Counter.Add(int64(mb.Len()))\n\treturn w.Writer.WriteMultiBuffer(mb)\n}\n\nfunc (w *SizeStatWriter) Close() error {\n\treturn common.Close(w.Writer)\n}\n\nfunc (w *SizeStatWriter) Interrupt() {\n\tcommon.Interrupt(w.Writer)\n}\n"
  },
  {
    "path": "core/xray/app/dispatcher/stats_test.go",
    "content": "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/common\"\n\t\"github.com/xtls/xray-core/common/buf\"\n)\n\ntype TestCounter int64\n\nfunc (c *TestCounter) Value() int64 {\n\treturn int64(*c)\n}\n\nfunc (c *TestCounter) Add(v int64) int64 {\n\tx := int64(*c) + v\n\t*c = TestCounter(x)\n\treturn x\n}\n\nfunc (c *TestCounter) Set(v int64) int64 {\n\t*c = TestCounter(v)\n\treturn v\n}\n\nfunc TestStatsWriter(t *testing.T) {\n\tvar c TestCounter\n\twriter := &SizeStatWriter{\n\t\tCounter: &c,\n\t\tWriter:  buf.Discard,\n\t}\n\n\tmb := buf.MergeBytes(nil, []byte(\"abcd\"))\n\tcommon.Must(writer.WriteMultiBuffer(mb))\n\n\tmb = buf.MergeBytes(nil, []byte(\"efg\"))\n\tcommon.Must(writer.WriteMultiBuffer(mb))\n\n\tif c.Value() != 7 {\n\t\tt.Fatal(\"unexpected counter value. want 7, but got \", c.Value())\n\t}\n}\n"
  },
  {
    "path": "core/xray/distro/all/all.go",
    "content": "package all\n\nimport (\n\t// The following are necessary as they register handlers in their init functions.\n\n\t// Mandatory features. Can't remove unless there are replacements.\n\t_ \"github.com/xtls/xray-core/app/dispatcher\"\n\t_ \"github.com/xtls/xray-core/app/proxyman/inbound\"\n\t_ \"github.com/xtls/xray-core/app/proxyman/outbound\"\n\n\t// Default commander and all its services. This is an optional feature.\n\t//_ \"github.com/xtls/xray-core/app/commander\"\n\t//_ \"github.com/xtls/xray-core/app/log/command\"\n\t//_ \"github.com/xtls/xray-core/app/proxyman/command\"\n\t//_ \"github.com/xtls/xray-core/app/stats/command\"\n\n\t// Developer preview services\n\t//_ \"github.com/xtls/xray-core/app/observatory/command\"\n\n\t// Other optional features.\n\t_ \"github.com/xtls/xray-core/app/dns\"\n\t_ \"github.com/xtls/xray-core/app/dns/fakedns\"\n\t_ \"github.com/xtls/xray-core/app/log\"\n\t_ \"github.com/xtls/xray-core/app/metrics\"\n\t_ \"github.com/xtls/xray-core/app/policy\"\n\t_ \"github.com/xtls/xray-core/app/reverse\"\n\t_ \"github.com/xtls/xray-core/app/router\"\n\t_ \"github.com/xtls/xray-core/app/stats\"\n\n\t// Fix dependency cycle caused by core import in internet package\n\t_ \"github.com/xtls/xray-core/transport/internet/tagged/taggedimpl\"\n\n\t// Developer preview features\n\t//_ \"github.com/xtls/xray-core/app/observatory\"\n\n\t// Inbound and outbound proxies.\n\t_ \"github.com/xtls/xray-core/proxy/blackhole\"\n\t_ \"github.com/xtls/xray-core/proxy/dns\"\n\t_ \"github.com/xtls/xray-core/proxy/dokodemo\"\n\t_ \"github.com/xtls/xray-core/proxy/freedom\"\n\t_ \"github.com/xtls/xray-core/proxy/http\"\n\t_ \"github.com/xtls/xray-core/proxy/loopback\"\n\t_ \"github.com/xtls/xray-core/proxy/shadowsocks\"\n\t_ \"github.com/xtls/xray-core/proxy/socks\"\n\t_ \"github.com/xtls/xray-core/proxy/trojan\"\n\t_ \"github.com/xtls/xray-core/proxy/vless/inbound\"\n\t_ \"github.com/xtls/xray-core/proxy/vless/outbound\"\n\t_ \"github.com/xtls/xray-core/proxy/vmess/inbound\"\n\t_ \"github.com/xtls/xray-core/proxy/vmess/outbound\"\n\n\t//_ \"github.com/xtls/xray-core/proxy/wireguard\"\n\n\t// Transports\n\t//_ \"github.com/xtls/xray-core/transport/internet/domainsocket\"\n\t_ \"github.com/xtls/xray-core/transport/internet/grpc\"\n\t_ \"github.com/xtls/xray-core/transport/internet/http\"\n\n\t//_ \"github.com/xtls/xray-core/transport/internet/kcp\"\n\t//_ \"github.com/xtls/xray-core/transport/internet/quic\"\n\t_ \"github.com/xtls/xray-core/transport/internet/reality\"\n\t_ \"github.com/xtls/xray-core/transport/internet/tcp\"\n\t_ \"github.com/xtls/xray-core/transport/internet/tls\"\n\t_ \"github.com/xtls/xray-core/transport/internet/udp\"\n\t_ \"github.com/xtls/xray-core/transport/internet/websocket\"\n\n\t// Transport headers\n\t_ \"github.com/xtls/xray-core/transport/internet/headers/http\"\n\t_ \"github.com/xtls/xray-core/transport/internet/headers/noop\"\n\t_ \"github.com/xtls/xray-core/transport/internet/headers/srtp\"\n\t_ \"github.com/xtls/xray-core/transport/internet/headers/tls\"\n\t_ \"github.com/xtls/xray-core/transport/internet/headers/utp\"\n\t_ \"github.com/xtls/xray-core/transport/internet/headers/wechat\"\n\t_ \"github.com/xtls/xray-core/transport/internet/headers/wireguard\"\n)\n"
  },
  {
    "path": "core/xray/inbound.go",
    "content": "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/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\t\"github.com/goccy/go-json\"\n\t\"github.com/xtls/xray-core/common/net\"\n\t\"github.com/xtls/xray-core/core\"\n\tcoreConf \"github.com/xtls/xray-core/infra/conf\"\n)\n\n// BuildInbound build Inbound config for different protocol\nfunc buildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag string) (*core.InboundHandlerConfig, error) {\n\tin := &coreConf.InboundDetourConfig{}\n\t// Set network protocol\n\tt := coreConf.TransportProtocol(nodeInfo.Network)\n\tin.StreamSetting = &coreConf.StreamConfig{Network: &t}\n\tvar err error\n\tswitch nodeInfo.Type {\n\tcase \"v2ray\":\n\t\terr = buildV2ray(config, nodeInfo, in)\n\tcase \"trojan\":\n\t\terr = buildTrojan(config, in)\n\tcase \"shadowsocks\":\n\t\terr = buildShadowsocks(config, nodeInfo, in)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupported node type: %s, Only support: V2ray, Trojan, Shadowsocks\", nodeInfo.Type)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Set server port\n\tin.PortList = &coreConf.PortList{\n\t\tRange: []coreConf.PortRange{{From: uint32(nodeInfo.Port), To: uint32(nodeInfo.Port)}},\n\t}\n\t// Set Listen IP address\n\tipAddress := net.ParseAddress(config.ListenIP)\n\tin.ListenOn = &coreConf.Address{Address: ipAddress}\n\t// Set SniffingConfig\n\tsniffingConfig := &coreConf.SniffingConfig{\n\t\tEnabled:      true,\n\t\tDestOverride: &coreConf.StringList{\"http\", \"tls\"},\n\t}\n\tif config.XrayOptions.DisableSniffing {\n\t\tsniffingConfig.Enabled = false\n\t}\n\tin.SniffingConfig = sniffingConfig\n\tif *in.StreamSetting.Network == \"tcp\" {\n\t\tif in.StreamSetting.TCPSettings != nil {\n\t\t\tin.StreamSetting.TCPSettings.AcceptProxyProtocol = config.XrayOptions.EnableProxyProtocol\n\t\t} else {\n\t\t\ttcpSetting := &coreConf.TCPConfig{\n\t\t\t\tAcceptProxyProtocol: config.XrayOptions.EnableProxyProtocol,\n\t\t\t} //Enable proxy protocol\n\t\t\tin.StreamSetting.TCPSettings = tcpSetting\n\t\t}\n\t} else if *in.StreamSetting.Network == \"ws\" {\n\t\tin.StreamSetting.WSSettings = &coreConf.WebSocketConfig{\n\t\t\tAcceptProxyProtocol: config.XrayOptions.EnableProxyProtocol} //Enable proxy protocol\n\t}\n\t// Set TLS or Reality settings\n\tif nodeInfo.Tls {\n\t\tif config.CertConfig == nil {\n\t\t\treturn nil, errors.New(\"the CertConfig is not vail\")\n\t\t}\n\t\tswitch config.CertConfig.CertMode {\n\t\tcase \"none\", \"\":\n\t\t\tbreak // disable\n\t\tcase \"reality\":\n\t\t\t// Reality\n\t\t\tin.StreamSetting.Security = \"reality\"\n\t\t\td, err := json.Marshal(config.CertConfig.RealityConfig.Dest)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"marshal reality dest error: %s\", err)\n\t\t\t}\n\t\t\tif len(config.CertConfig.RealityConfig.ShortIds) == 0 {\n\t\t\t\tconfig.CertConfig.RealityConfig.ShortIds = []string{\"\"}\n\t\t\t}\n\t\t\tin.StreamSetting.REALITYSettings = &coreConf.REALITYConfig{\n\t\t\t\tDest:         d,\n\t\t\t\tXver:         config.CertConfig.RealityConfig.Xver,\n\t\t\t\tServerNames:  config.CertConfig.RealityConfig.ServerNames,\n\t\t\t\tPrivateKey:   config.CertConfig.RealityConfig.PrivateKey,\n\t\t\t\tMinClientVer: config.CertConfig.RealityConfig.MinClientVer,\n\t\t\t\tMaxClientVer: config.CertConfig.RealityConfig.MaxClientVer,\n\t\t\t\tMaxTimeDiff:  config.CertConfig.RealityConfig.MaxTimeDiff,\n\t\t\t\tShortIds:     config.CertConfig.RealityConfig.ShortIds,\n\t\t\t}\n\t\t\tbreak\n\t\tcase \"remote\":\n\t\t\tif nodeInfo.ExtraConfig.EnableReality == \"true\" {\n\t\t\t\trc := nodeInfo.ExtraConfig.RealityConfig\n\t\t\t\tin.StreamSetting.Security = \"reality\"\n\t\t\t\td, err := json.Marshal(rc.Dest)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"marshal reality dest error: %s\", err)\n\t\t\t\t}\n\t\t\t\tif len(rc.ShortIds) == 0 {\n\t\t\t\t\trc.ShortIds = []string{\"\"}\n\t\t\t\t}\n\t\t\t\tXver, _ := strconv.ParseUint(rc.Xver, 10, 64)\n\t\t\t\tMaxTimeDiff, _ := strconv.ParseUint(rc.Xver, 10, 64)\n\t\t\t\tin.StreamSetting.REALITYSettings = &coreConf.REALITYConfig{\n\t\t\t\t\tDest:         d,\n\t\t\t\t\tXver:         Xver,\n\t\t\t\t\tServerNames:  rc.ServerNames,\n\t\t\t\t\tPrivateKey:   rc.PrivateKey,\n\t\t\t\t\tMinClientVer: rc.MinClientVer,\n\t\t\t\t\tMaxClientVer: rc.MaxClientVer,\n\t\t\t\t\tMaxTimeDiff:  MaxTimeDiff,\n\t\t\t\t\tShortIds:     rc.ShortIds,\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\tdefault:\n\t\t\t{\n\t\t\t\t// Normal tls\n\t\t\t\tin.StreamSetting.Security = \"tls\"\n\t\t\t\tin.StreamSetting.TLSSettings = &coreConf.TLSConfig{\n\t\t\t\t\tCerts: []*coreConf.TLSCertConfig{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCertFile:     config.CertConfig.CertFile,\n\t\t\t\t\t\t\tKeyFile:      config.CertConfig.KeyFile,\n\t\t\t\t\t\t\tOcspStapling: 3600,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tRejectUnknownSNI: config.CertConfig.RejectUnknownSni,\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// Support ProxyProtocol for any transport protocol\n\tif *in.StreamSetting.Network != \"tcp\" &&\n\t\t*in.StreamSetting.Network != \"ws\" &&\n\t\tconfig.XrayOptions.EnableProxyProtocol {\n\t\tsocketConfig := &coreConf.SocketConfig{\n\t\t\tAcceptProxyProtocol: config.XrayOptions.EnableProxyProtocol,\n\t\t\tTFO:                 config.XrayOptions.EnableTFO,\n\t\t} //Enable proxy protocol\n\t\tin.StreamSetting.SocketSettings = socketConfig\n\t}\n\tin.Tag = tag\n\treturn in.Build()\n}\n\nfunc buildV2ray(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error {\n\tif nodeInfo.ExtraConfig.EnableVless == \"true\" {\n\t\t//Set vless\n\t\tinbound.Protocol = \"vless\"\n\t\tif config.XrayOptions.EnableFallback {\n\t\t\t// Set fallback\n\t\t\tfallbackConfigs, err := buildVlessFallbacks(config.XrayOptions.FallBackConfigs)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ts, err := json.Marshal(&coreConf.VLessInboundConfig{\n\t\t\t\tDecryption: \"none\",\n\t\t\t\tFallbacks:  fallbackConfigs,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"marshal vless fallback config error: %s\", err)\n\t\t\t}\n\t\t\tinbound.Settings = (*json.RawMessage)(&s)\n\t\t} else {\n\t\t\tvar err error\n\t\t\ts, err := json.Marshal(&coreConf.VLessInboundConfig{\n\t\t\t\tDecryption: \"none\",\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"marshal vless config error: %s\", err)\n\t\t\t}\n\t\t\tinbound.Settings = (*json.RawMessage)(&s)\n\t\t}\n\t} else {\n\t\t// Set vmess\n\t\tinbound.Protocol = \"vmess\"\n\t\tvar err error\n\t\ts, err := json.Marshal(&coreConf.VMessInboundConfig{})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"marshal vmess settings error: %s\", err)\n\t\t}\n\t\tinbound.Settings = (*json.RawMessage)(&s)\n\t}\n\tif len(nodeInfo.NetworkSettings) == 0 {\n\t\treturn nil\n\t}\n\tswitch nodeInfo.Network {\n\tcase \"tcp\":\n\t\terr := json.Unmarshal(nodeInfo.NetworkSettings, &inbound.StreamSetting.TCPSettings)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unmarshal tcp settings error: %s\", err)\n\t\t}\n\tcase \"ws\":\n\t\terr := json.Unmarshal(nodeInfo.NetworkSettings, &inbound.StreamSetting.WSSettings)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unmarshal ws settings error: %s\", err)\n\t\t}\n\tcase \"grpc\":\n\t\terr := json.Unmarshal(nodeInfo.NetworkSettings, &inbound.StreamSetting.GRPCConfig)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unmarshal grpc settings error: %s\", err)\n\t\t}\n\tdefault:\n\t\treturn errors.New(\"the network type is not vail\")\n\t}\n\treturn nil\n}\n\nfunc buildTrojan(config *conf.ControllerConfig, inbound *coreConf.InboundDetourConfig) error {\n\tinbound.Protocol = \"trojan\"\n\tif config.XrayOptions.EnableFallback {\n\t\t// Set fallback\n\t\tfallbackConfigs, err := buildTrojanFallbacks(config.XrayOptions.FallBackConfigs)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ts, err := json.Marshal(&coreConf.TrojanServerConfig{\n\t\t\tFallbacks: fallbackConfigs,\n\t\t})\n\t\tinbound.Settings = (*json.RawMessage)(&s)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"marshal trojan fallback config error: %s\", err)\n\t\t}\n\t} else {\n\t\ts := []byte(\"{}\")\n\t\tinbound.Settings = (*json.RawMessage)(&s)\n\t}\n\tt := coreConf.TransportProtocol(\"tcp\")\n\tinbound.StreamSetting = &coreConf.StreamConfig{Network: &t}\n\treturn nil\n}\n\nfunc buildShadowsocks(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error {\n\tinbound.Protocol = \"shadowsocks\"\n\tsettings := &coreConf.ShadowsocksServerConfig{\n\t\tCipher: nodeInfo.Cipher,\n\t}\n\tp := make([]byte, 32)\n\t_, err := rand.Read(p)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"generate random password error: %s\", err)\n\t}\n\trandomPasswd := hex.EncodeToString(p)\n\tcipher := nodeInfo.Cipher\n\tif nodeInfo.ServerKey != \"\" {\n\t\tsettings.Password = nodeInfo.ServerKey\n\t\trandomPasswd = base64.StdEncoding.EncodeToString([]byte(randomPasswd))\n\t\tcipher = \"\"\n\t}\n\tdefaultSSuser := &coreConf.ShadowsocksUserConfig{\n\t\tCipher:   cipher,\n\t\tPassword: randomPasswd,\n\t}\n\tsettings.Users = append(settings.Users, defaultSSuser)\n\tsettings.NetworkList = &coreConf.NetworkList{\"tcp\", \"udp\"}\n\tsettings.IVCheck = true\n\tif config.XrayOptions.DisableIVCheck {\n\t\tsettings.IVCheck = false\n\t}\n\tt := coreConf.TransportProtocol(\"tcp\")\n\tinbound.StreamSetting = &coreConf.StreamConfig{Network: &t}\n\ts, err := json.Marshal(settings)\n\tinbound.Settings = (*json.RawMessage)(&s)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"marshal shadowsocks settings error: %s\", err)\n\t}\n\treturn nil\n}\n\nfunc buildVlessFallbacks(fallbackConfigs []conf.FallBackConfig) ([]*coreConf.VLessInboundFallback, error) {\n\tif fallbackConfigs == nil {\n\t\treturn nil, fmt.Errorf(\"you must provide FallBackConfigs\")\n\t}\n\tvlessFallBacks := make([]*coreConf.VLessInboundFallback, len(fallbackConfigs))\n\tfor i, c := range fallbackConfigs {\n\t\tif c.Dest == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"dest is required for fallback fialed\")\n\t\t}\n\t\tvar dest json.RawMessage\n\t\tdest, err := json.Marshal(c.Dest)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"marshal dest %s config fialed: %s\", dest, err)\n\t\t}\n\t\tvlessFallBacks[i] = &coreConf.VLessInboundFallback{\n\t\t\tName: c.SNI,\n\t\t\tAlpn: c.Alpn,\n\t\t\tPath: c.Path,\n\t\t\tDest: dest,\n\t\t\tXver: c.ProxyProtocolVer,\n\t\t}\n\t}\n\treturn vlessFallBacks, nil\n}\n\nfunc buildTrojanFallbacks(fallbackConfigs []conf.FallBackConfig) ([]*coreConf.TrojanInboundFallback, error) {\n\tif fallbackConfigs == nil {\n\t\treturn nil, fmt.Errorf(\"you must provide FallBackConfigs\")\n\t}\n\n\ttrojanFallBacks := make([]*coreConf.TrojanInboundFallback, len(fallbackConfigs))\n\tfor i, c := range fallbackConfigs {\n\n\t\tif c.Dest == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"dest is required for fallback fialed\")\n\t\t}\n\n\t\tvar dest json.RawMessage\n\t\tdest, err := json.Marshal(c.Dest)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"marshal dest %s config fialed: %s\", dest, err)\n\t\t}\n\t\ttrojanFallBacks[i] = &coreConf.TrojanInboundFallback{\n\t\t\tName: c.SNI,\n\t\t\tAlpn: c.Alpn,\n\t\t\tPath: c.Path,\n\t\t\tDest: dest,\n\t\t\tXver: c.ProxyProtocolVer,\n\t\t}\n\t}\n\treturn trojanFallBacks, nil\n}\n"
  },
  {
    "path": "core/xray/node.go",
    "content": "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\"github.com/xtls/xray-core/core\"\n\t\"github.com/xtls/xray-core/features/inbound\"\n\t\"github.com/xtls/xray-core/features/outbound\"\n)\n\nfunc (c *Core) AddNode(tag string, info *panel.NodeInfo, config *conf.ControllerConfig) error {\n\tinboundConfig, err := buildInbound(config, info, tag)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"build inbound error: %s\", err)\n\t}\n\terr = c.addInbound(inboundConfig)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"add inbound error: %s\", err)\n\t}\n\toutBoundConfig, err := buildOutbound(config, tag)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"build outbound error: %s\", err)\n\t}\n\terr = c.addOutbound(outBoundConfig)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"add outbound error: %s\", err)\n\t}\n\treturn nil\n}\n\nfunc (c *Core) addInbound(config *core.InboundHandlerConfig) error {\n\trawHandler, err := core.CreateObject(c.Server, config)\n\tif err != nil {\n\t\treturn err\n\t}\n\thandler, ok := rawHandler.(inbound.Handler)\n\tif !ok {\n\t\treturn fmt.Errorf(\"not an InboundHandler: %s\", err)\n\t}\n\tif err := c.ihm.AddHandler(context.Background(), handler); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (c *Core) addOutbound(config *core.OutboundHandlerConfig) error {\n\trawHandler, err := core.CreateObject(c.Server, config)\n\tif err != nil {\n\t\treturn err\n\t}\n\thandler, ok := rawHandler.(outbound.Handler)\n\tif !ok {\n\t\treturn fmt.Errorf(\"not an InboundHandler: %s\", err)\n\t}\n\tif err := c.ohm.AddHandler(context.Background(), handler); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (c *Core) DelNode(tag string) error {\n\terr := c.removeInbound(tag)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"remove in error: %s\", err)\n\t}\n\terr = c.removeOutbound(tag)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"remove out error: %s\", err)\n\t}\n\treturn nil\n}\n\nfunc (c *Core) removeInbound(tag string) error {\n\treturn c.ihm.RemoveHandler(context.Background(), tag)\n}\n\nfunc (c *Core) removeOutbound(tag string) error {\n\terr := c.ohm.RemoveHandler(context.Background(), tag)\n\treturn err\n}\n"
  },
  {
    "path": "core/xray/outbound.go",
    "content": "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-core/common/net\"\n\t\"github.com/xtls/xray-core/core\"\n\t\"github.com/xtls/xray-core/infra/conf\"\n)\n\n// BuildOutbound build freedom outbund config for addoutbound\nfunc buildOutbound(config *conf2.ControllerConfig, tag string) (*core.OutboundHandlerConfig, error) {\n\toutboundDetourConfig := &conf.OutboundDetourConfig{}\n\toutboundDetourConfig.Protocol = \"freedom\"\n\toutboundDetourConfig.Tag = tag\n\n\t// Build Send IP address\n\tif config.SendIP != \"\" {\n\t\tipAddress := net.ParseAddress(config.SendIP)\n\t\toutboundDetourConfig.SendThrough = &conf.Address{Address: ipAddress}\n\t}\n\n\t// Freedom Protocol setting\n\tvar domainStrategy = \"Asis\"\n\tif config.XrayOptions.EnableDNS {\n\t\tif config.XrayOptions.DNSType != \"\" {\n\t\t\tdomainStrategy = config.XrayOptions.DNSType\n\t\t} else {\n\t\t\tdomainStrategy = \"UseIP\"\n\t\t}\n\t}\n\tproxySetting := &conf.FreedomConfig{\n\t\tDomainStrategy: domainStrategy,\n\t}\n\tvar setting json.RawMessage\n\tsetting, err := json.Marshal(proxySetting)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"marshal proxy config error: %s\", err)\n\t}\n\toutboundDetourConfig.Settings = &setting\n\treturn outboundDetourConfig.Build()\n}\n"
  },
  {
    "path": "core/xray/ss.go",
    "content": "package xray\n\nimport (\n\t\"encoding/base64\"\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/common/format\"\n\t\"github.com/xtls/xray-core/common/protocol\"\n\t\"github.com/xtls/xray-core/common/serial\"\n\t\"github.com/xtls/xray-core/proxy/shadowsocks\"\n\t\"github.com/xtls/xray-core/proxy/shadowsocks_2022\"\n\t\"strings\"\n)\n\nfunc buildSSUsers(tag string, userInfo []panel.UserInfo, cypher string, serverKey string) (users []*protocol.User) {\n\tusers = make([]*protocol.User, len(userInfo))\n\tfor i := range userInfo {\n\t\tusers[i] = buildSSUser(tag, &userInfo[i], cypher, serverKey)\n\t}\n\treturn users\n}\n\nfunc buildSSUser(tag string, userInfo *panel.UserInfo, cypher string, serverKey string) (user *protocol.User) {\n\tif serverKey == \"\" {\n\t\tssAccount := &shadowsocks.Account{\n\t\t\tPassword:   userInfo.Uuid,\n\t\t\tCipherType: getCipherFromString(cypher),\n\t\t}\n\t\treturn &protocol.User{\n\t\t\tLevel:   0,\n\t\t\tEmail:   format.UserTag(tag, userInfo.Uuid),\n\t\t\tAccount: serial.ToTypedMessage(ssAccount),\n\t\t}\n\t} else {\n\t\tvar keyLength int\n\t\tswitch cypher {\n\t\tcase \"2022-blake3-aes-128-gcm\":\n\t\t\tkeyLength = 16\n\t\tcase \"2022-blake3-aes-256-gcm\":\n\t\t\tkeyLength = 32\n\t\t}\n\t\tssAccount := &shadowsocks_2022.User{\n\t\t\tKey: base64.StdEncoding.EncodeToString([]byte(userInfo.Uuid[:keyLength])),\n\t\t}\n\t\treturn &protocol.User{\n\t\t\tLevel:   0,\n\t\t\tEmail:   format.UserTag(tag, userInfo.Uuid),\n\t\t\tAccount: serial.ToTypedMessage(ssAccount),\n\t\t}\n\t}\n}\n\nfunc getCipherFromString(c string) shadowsocks.CipherType {\n\tswitch strings.ToLower(c) {\n\tcase \"aes-128-gcm\", \"aead_aes_128_gcm\":\n\t\treturn shadowsocks.CipherType_AES_128_GCM\n\tcase \"aes-256-gcm\", \"aead_aes_256_gcm\":\n\t\treturn shadowsocks.CipherType_AES_256_GCM\n\tcase \"chacha20-poly1305\", \"aead_chacha20_poly1305\", \"chacha20-ietf-poly1305\":\n\t\treturn shadowsocks.CipherType_CHACHA20_POLY1305\n\tcase \"none\", \"plain\":\n\t\treturn shadowsocks.CipherType_NONE\n\tdefault:\n\t\treturn shadowsocks.CipherType_UNKNOWN\n\t}\n}\n"
  },
  {
    "path": "core/xray/trojan.go",
    "content": "package xray\n\nimport (\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/common/format\"\n\t\"github.com/xtls/xray-core/common/protocol\"\n\t\"github.com/xtls/xray-core/common/serial\"\n\t\"github.com/xtls/xray-core/proxy/trojan\"\n)\n\nfunc buildTrojanUsers(tag string, userInfo []panel.UserInfo) (users []*protocol.User) {\n\tusers = make([]*protocol.User, len(userInfo))\n\tfor i := range userInfo {\n\t\tusers[i] = buildTrojanUser(tag, &(userInfo)[i])\n\t}\n\treturn users\n}\n\nfunc buildTrojanUser(tag string, userInfo *panel.UserInfo) (user *protocol.User) {\n\ttrojanAccount := &trojan.Account{\n\t\tPassword: userInfo.Uuid,\n\t}\n\treturn &protocol.User{\n\t\tLevel:   0,\n\t\tEmail:   format.UserTag(tag, userInfo.Uuid),\n\t\tAccount: serial.ToTypedMessage(trojanAccount),\n\t}\n}\n"
  },
  {
    "path": "core/xray/user.go",
    "content": "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/format\"\n\tvCore \"github.com/Yuzuki616/V2bX/core\"\n\t\"github.com/xtls/xray-core/common/protocol\"\n\t\"github.com/xtls/xray-core/proxy\"\n)\n\nfunc (c *Core) GetUserManager(tag string) (proxy.UserManager, error) {\n\thandler, err := c.ihm.GetHandler(context.Background(), tag)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"no such inbound tag: %s\", err)\n\t}\n\tinboundInstance, ok := handler.(proxy.GetInbound)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"handler %s is not implement proxy.GetInbound\", tag)\n\t}\n\tuserManager, ok := inboundInstance.GetInbound().(proxy.UserManager)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"handler %s is not implement proxy.UserManager\", tag)\n\t}\n\treturn userManager, nil\n}\n\nfunc (c *Core) DelUsers(users []panel.UserInfo, tag string) error {\n\tuserManager, err := c.GetUserManager(tag)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get user manager error: %s\", err)\n\t}\n\tvar up, down, user string\n\tfor i := range users {\n\t\tuser = format.UserTag(tag, users[i].Uuid)\n\t\terr = userManager.RemoveUser(context.Background(), user)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tup = \"user>>>\" + user + \">>>traffic>>>uplink\"\n\t\tdown = \"user>>>\" + user + \">>>traffic>>>downlink\"\n\t\tc.shm.UnregisterCounter(up)\n\t\tc.shm.UnregisterCounter(down)\n\t}\n\treturn nil\n}\n\nfunc (c *Core) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) {\n\tupName := \"user>>>\" + format.UserTag(tag, uuid) + \">>>traffic>>>uplink\"\n\tdownName := \"user>>>\" + format.UserTag(tag, uuid) + \">>>traffic>>>downlink\"\n\tupCounter := c.shm.GetCounter(upName)\n\tdownCounter := c.shm.GetCounter(downName)\n\tif reset {\n\t\tif upCounter != nil {\n\t\t\tup = upCounter.Set(0)\n\t\t}\n\t\tif downCounter != nil {\n\t\t\tdown = downCounter.Set(0)\n\t\t}\n\t} else {\n\t\tif upCounter != nil {\n\t\t\tup = upCounter.Value()\n\t\t}\n\t\tif downCounter != nil {\n\t\t\tdown = downCounter.Value()\n\t\t}\n\t}\n\treturn up, down\n}\n\nfunc (c *Core) AddUsers(p *vCore.AddUsersParams) (added int, err error) {\n\tusers := make([]*protocol.User, 0, len(p.UserInfo))\n\tswitch p.NodeInfo.Type {\n\tcase \"v2ray\":\n\t\tif p.NodeInfo.ExtraConfig.EnableVless == \"true\" {\n\t\t\tusers = buildVlessUsers(p.Tag, p.UserInfo, p.NodeInfo.ExtraConfig.VlessFlow)\n\t\t} else {\n\t\t\tusers = buildVmessUsers(p.Tag, p.UserInfo)\n\t\t}\n\tcase \"trojan\":\n\t\tusers = buildTrojanUsers(p.Tag, p.UserInfo)\n\tcase \"shadowsocks\":\n\t\tusers = buildSSUsers(p.Tag,\n\t\t\tp.UserInfo,\n\t\t\tp.NodeInfo.Cipher,\n\t\t\tp.NodeInfo.ServerKey)\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"unsupported node type: %s\", p.NodeInfo.Type)\n\t}\n\tman, err := c.GetUserManager(p.Tag)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"get user manager error: %s\", err)\n\t}\n\tfor _, u := range users {\n\t\tmUser, err := u.ToMemoryUser()\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\terr = man.AddUser(context.Background(), mUser)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\treturn len(users), nil\n}\n"
  },
  {
    "path": "core/xray/vmess.go",
    "content": "package xray\n\nimport (\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n\t\"github.com/Yuzuki616/V2bX/common/format\"\n\t\"github.com/xtls/xray-core/common/protocol\"\n\t\"github.com/xtls/xray-core/common/serial\"\n\t\"github.com/xtls/xray-core/infra/conf\"\n\t\"github.com/xtls/xray-core/proxy/vless\"\n)\n\nfunc buildVmessUsers(tag string, userInfo []panel.UserInfo) (users []*protocol.User) {\n\tusers = make([]*protocol.User, len(userInfo))\n\tfor i, user := range userInfo {\n\t\tusers[i] = buildVmessUser(tag, &user)\n\t}\n\treturn users\n}\n\nfunc buildVmessUser(tag string, userInfo *panel.UserInfo) (user *protocol.User) {\n\tvmessAccount := &conf.VMessAccount{\n\t\tID:       userInfo.Uuid,\n\t\tSecurity: \"auto\",\n\t}\n\treturn &protocol.User{\n\t\tLevel:   0,\n\t\tEmail:   format.UserTag(tag, userInfo.Uuid), // Uid: InboundTag|email\n\t\tAccount: serial.ToTypedMessage(vmessAccount.Build()),\n\t}\n}\n\nfunc buildVlessUsers(tag string, userInfo []panel.UserInfo, flow string) (users []*protocol.User) {\n\tusers = make([]*protocol.User, len(userInfo))\n\tfor i := range userInfo {\n\t\tusers[i] = buildVlessUser(tag, &(userInfo)[i], flow)\n\t}\n\treturn users\n}\n\nfunc buildVlessUser(tag string, userInfo *panel.UserInfo, flow string) (user *protocol.User) {\n\tvlessAccount := &vless.Account{\n\t\tId: userInfo.Uuid,\n\t}\n\tvlessAccount.Flow = flow\n\treturn &protocol.User{\n\t\tLevel:   0,\n\t\tEmail:   format.UserTag(tag, userInfo.Uuid),\n\t\tAccount: serial.ToTypedMessage(vlessAccount),\n\t}\n}\n"
  },
  {
    "path": "core/xray/xray.go",
    "content": "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.com/Yuzuki616/V2bX/core/xray/app/dispatcher\"\n\t_ \"github.com/Yuzuki616/V2bX/core/xray/distro/all\"\n\t\"github.com/goccy/go-json\"\n\tlog \"github.com/sirupsen/logrus\"\n\t\"github.com/xtls/xray-core/app/proxyman\"\n\t\"github.com/xtls/xray-core/app/stats\"\n\t\"github.com/xtls/xray-core/common/serial\"\n\t\"github.com/xtls/xray-core/core\"\n\t\"github.com/xtls/xray-core/features/inbound\"\n\t\"github.com/xtls/xray-core/features/outbound\"\n\t\"github.com/xtls/xray-core/features/routing\"\n\tstatsFeature \"github.com/xtls/xray-core/features/stats\"\n\tcoreConf \"github.com/xtls/xray-core/infra/conf\"\n)\n\nfunc init() {\n\tvCore.RegisterCore(\"xray\", New)\n}\n\n// Core Structure\ntype Core struct {\n\taccess     sync.Mutex\n\tServer     *core.Instance\n\tihm        inbound.Manager\n\tohm        outbound.Manager\n\tshm        statsFeature.Manager\n\tdispatcher *dispatcher.DefaultDispatcher\n}\n\nfunc New(c *conf.CoreConfig) (vCore.Core, error) {\n\treturn &Core{Server: getCore(c.XrayConfig)}, nil\n}\n\nfunc parseConnectionConfig(c *conf.ConnectionConfig) (policy *coreConf.Policy) {\n\tpolicy = &coreConf.Policy{\n\t\tStatsUserUplink:   true,\n\t\tStatsUserDownlink: true,\n\t\tHandshake:         &c.Handshake,\n\t\tConnectionIdle:    &c.ConnIdle,\n\t\tUplinkOnly:        &c.UplinkOnly,\n\t\tDownlinkOnly:      &c.DownlinkOnly,\n\t\tBufferSize:        &c.BufferSize,\n\t}\n\treturn\n}\n\nfunc getCore(c *conf.XrayConfig) *core.Instance {\n\tos.Setenv(\"XRAY_LOCATION_ASSET\", c.AssetPath)\n\t// Log Config\n\tcoreLogConfig := &coreConf.LogConfig{}\n\tcoreLogConfig.LogLevel = c.LogConfig.Level\n\tcoreLogConfig.AccessLog = c.LogConfig.AccessPath\n\tcoreLogConfig.ErrorLog = c.LogConfig.ErrorPath\n\t// DNS config\n\tcoreDnsConfig := &coreConf.DNSConfig{}\n\tos.Setenv(\"XRAY_DNS_PATH\", \"\")\n\tif c.DnsConfigPath != \"\" {\n\t\tif f, err := os.Open(c.DnsConfigPath); err != nil {\n\t\t\tlog.WithField(\"err\", err).Panic(\"Failed to read DNS config file\")\n\t\t} else {\n\t\t\tif err = json.NewDecoder(f).Decode(coreDnsConfig); err != nil {\n\t\t\t\tlog.WithField(\"err\", err).Panic(\"Failed to unmarshal DNS config\")\n\t\t\t}\n\t\t}\n\t\tos.Setenv(\"XRAY_DNS_PATH\", c.DnsConfigPath)\n\t}\n\tdnsConfig, err := coreDnsConfig.Build()\n\tif err != nil {\n\t\tlog.WithField(\"err\", err).Panic(\"Failed to understand DNS config, Please check: https://xtls.github.io/config/dns.html for help\")\n\t}\n\t// Routing config\n\tcoreRouterConfig := &coreConf.RouterConfig{}\n\tif c.RouteConfigPath != \"\" {\n\t\tif f, err := os.Open(c.RouteConfigPath); err != nil {\n\t\t\tlog.WithField(\"err\", err).Panic(\"Failed to read Routing config file\")\n\t\t} else {\n\t\t\tif err = json.NewDecoder(f).Decode(coreRouterConfig); err != nil {\n\t\t\t\tlog.WithField(\"err\", err).Panic(\"Failed to unmarshal Routing config\")\n\t\t\t}\n\t\t}\n\t}\n\trouteConfig, err := coreRouterConfig.Build()\n\tif err != nil {\n\t\tlog.WithField(\"err\", err).Panic(\"Failed to understand Routing config  Please check: https://xtls.github.io/config/routing.html\")\n\t}\n\t// Custom Inbound config\n\tvar coreCustomInboundConfig []coreConf.InboundDetourConfig\n\tif c.InboundConfigPath != \"\" {\n\t\tif f, err := os.Open(c.InboundConfigPath); err != nil {\n\t\t\tlog.WithField(\"err\", err).Panic(\"Failed to read Custom Inbound config file\")\n\t\t} else {\n\t\t\tif err = json.NewDecoder(f).Decode(&coreCustomInboundConfig); err != nil {\n\t\t\t\tlog.WithField(\"err\", err).Panic(\"Failed to unmarshal Custom Inbound config\")\n\t\t\t}\n\t\t}\n\t}\n\tvar inBoundConfig []*core.InboundHandlerConfig\n\tfor _, config := range coreCustomInboundConfig {\n\t\toc, err := config.Build()\n\t\tif err != nil {\n\t\t\tlog.WithField(\"err\", err).Panic(\"Failed to understand Inbound config, Please check: https://xtls.github.io/config/inbound.html for help\")\n\t\t}\n\t\tinBoundConfig = append(inBoundConfig, oc)\n\t}\n\t// Custom Outbound config\n\tvar coreCustomOutboundConfig []coreConf.OutboundDetourConfig\n\tif c.OutboundConfigPath != \"\" {\n\t\tif f, err := os.Open(c.OutboundConfigPath); err != nil {\n\t\t\tlog.WithField(\"err\", err).Panic(\"Failed to read Custom Outbound config file\")\n\t\t} else {\n\t\t\tif err = json.NewDecoder(f).Decode(&coreCustomOutboundConfig); err != nil {\n\t\t\t\tlog.WithField(\"err\", err).Panic(\"Failed to unmarshal Custom Outbound config\")\n\t\t\t}\n\t\t}\n\t}\n\tvar outBoundConfig []*core.OutboundHandlerConfig\n\tfor _, config := range coreCustomOutboundConfig {\n\t\toc, err := config.Build()\n\t\tif err != nil {\n\t\t\tlog.WithField(\"err\", err).Panic(\"Failed to understand Outbound config, Please check: https://xtls.github.io/config/outbound.html for help\")\n\t\t}\n\t\toutBoundConfig = append(outBoundConfig, oc)\n\t}\n\t// Policy config\n\tlevelPolicyConfig := parseConnectionConfig(c.ConnectionConfig)\n\tcorePolicyConfig := &coreConf.PolicyConfig{}\n\tcorePolicyConfig.Levels = map[uint32]*coreConf.Policy{0: levelPolicyConfig}\n\tpolicyConfig, _ := corePolicyConfig.Build()\n\t// Build Core conf\n\tconfig := &core.Config{\n\t\tApp: []*serial.TypedMessage{\n\t\t\tserial.ToTypedMessage(coreLogConfig.Build()),\n\t\t\tserial.ToTypedMessage(&dispatcher.Config{}),\n\t\t\tserial.ToTypedMessage(&stats.Config{}),\n\t\t\tserial.ToTypedMessage(&proxyman.InboundConfig{}),\n\t\t\tserial.ToTypedMessage(&proxyman.OutboundConfig{}),\n\t\t\tserial.ToTypedMessage(policyConfig),\n\t\t\tserial.ToTypedMessage(dnsConfig),\n\t\t\tserial.ToTypedMessage(routeConfig),\n\t\t},\n\t\tInbound:  inBoundConfig,\n\t\tOutbound: outBoundConfig,\n\t}\n\tserver, err := core.New(config)\n\tif err != nil {\n\t\tlog.WithField(\"err\", err).Panic(\"failed to create instance\")\n\t}\n\tlog.Info(\"Xray Core Version: \", core.Version())\n\treturn server\n}\n\n// Start the Core\nfunc (c *Core) Start() error {\n\tc.access.Lock()\n\tdefer c.access.Unlock()\n\tif err := c.Server.Start(); err != nil {\n\t\treturn err\n\t}\n\tc.shm = c.Server.GetFeature(statsFeature.ManagerType()).(statsFeature.Manager)\n\tc.ihm = c.Server.GetFeature(inbound.ManagerType()).(inbound.Manager)\n\tc.ohm = c.Server.GetFeature(outbound.ManagerType()).(outbound.Manager)\n\tc.dispatcher = c.Server.GetFeature(routing.DispatcherType()).(*dispatcher.DefaultDispatcher)\n\treturn nil\n}\n\n// Close  the core\nfunc (c *Core) Close() error {\n\tc.access.Lock()\n\tdefer c.access.Unlock()\n\tc.ihm = nil\n\tc.ohm = nil\n\tc.shm = nil\n\tc.dispatcher = nil\n\terr := c.Server.Close()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (c *Core) Protocols() []string {\n\treturn []string{\n\t\t\"v2ray\",\n\t\t\"shadowsocks\",\n\t\t\"trojan\",\n\t}\n}\n"
  },
  {
    "path": "example/config.yml.example",
    "content": "CoreConfig:\n  Type: \"xray\" # Core type, default support \"xray\" and \"hy\". If you need many cores, use \" \" to split\n  XrayConfig:\n    Log:\n      Level: warning # Log level: none, error, warning, info, debug\n      AccessPath: # /etc/XrayR/access.Log\n      ErrorPath: # /etc/XrayR/error.log\n    DnsConfigPath: # /etc/XrayR/dns.json # Path to dns config, check https://xtls.github.io/config/dns.html for help\n    RouteConfigPath: # /etc/XrayR/route.json # Path to route config, check https://xtls.github.io/config/routing.html for help\n    InboundConfigPath: # /etc/XrayR/custom_inbound.json # Path to custom inbound config, check https://xtls.github.io/config/inbound.html for help\n    OutboundConfigPath: # /etc/XrayR/custom_outbound.json # Path to custom outbound config, check https://xtls.github.io/config/outbound.html for help\n    ConnectionConfig:\n      Handshake: 4 # Handshake time limit, Second\n      ConnIdle: 30 # Connection idle time limit, Second\n      UplinkOnly: 2 # Time limit when the connection downstream is closed, Second\n      DownlinkOnly: 4 # Time limit when the connection is closed after the uplink is closed, Second\n      BufferSize: 64 # The internal cache size of each connection, kB\nNodes:\n  - ApiConfig:\n      ApiHost: \"http://127.0.0.1:667\"\n      ApiKey: \"123\"\n      NodeID: 41\n      NodeType: V2ray # Node type: V2ray, Shadowsocks, Trojan\n      Timeout: 30 # Timeout for the api request\n      RuleListPath: # /etc/XrayR/rulelist Path to local rulelist file\n    ControllerConfig:\n      ListenIP: 0.0.0.0 # IP address you want to listen\n      SendIP: 0.0.0.0 # IP address you want to send pacakage\n      XrayOptions:\n        EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well\n        DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy\n        EnableTFO: false # Enable TCP Fast Open\n        EnableProxyProtocol: false # Only works for WebSocket and TCP\n        EnableFallback: false # Only support for Trojan and Vless\n        FallBackConfigs: # Support multiple fallbacks\n          - SNI: # TLS SNI(Server Name Indication), Empty for any\n            Alpn: # Alpn, Empty for any\n            Path: # HTTP PATH, Empty for any\n            Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details.\n            ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable\n      HyOptions:\n        Resolver: \"udp://1.1.1.1:53\" # DNS resolver address\n        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)\n        SendDevice: \"eth0\" # Bind device for outbound connections (usually requires root)\n      LimitConfig:\n        EnableRealtime: false # Check device limit on real time\n        SpeedLimit: 0 # Mbps, Local settings will replace remote settings, 0 means disable\n        DeviceLimit: 0 # Local settings will replace remote settings, 0 means disable\n        ConnLimit: 0 # Connecting limit, only working for TCP, 0mean\n        EnableIpRecorder: false # Enable online ip report\n        IpRecorderConfig:\n          Type: \"Recorder\" # Recorder type: Recorder, Redis\n          RecorderConfig:\n            Url: \"http://127.0.0.1:123\" # Report url\n            Token: \"123\" # Report token\n            Timeout: 10 # Report timeout, sec.\n          RedisConfig:\n            Address: \"127.0.0.1:6379\" # Redis address\n            Password: \"\" # Redis password\n            DB: 0 # Redis DB\n            Expiry: 60 # redis expiry time, sec.\n          Periodic: 60 # Report interval, sec.\n          EnableIpSync: false # Enable online ip sync\n        EnableDynamicSpeedLimit: false # Enable dynamic speed limit\n        DynamicSpeedLimitConfig:\n          Periodic: 60 # Time to check the user traffic , sec.\n          Traffic: 0 # Traffic limit, MB\n          SpeedLimit: 0 # Speed limit, Mbps\n          ExpireTime: 0 # Time limit, sec.\n      CertConfig:\n        CertMode: dns # Option about how to get certificate: none, file, http, dns, reality, remote. Choose \"none\" will forcedly disable the tls config.\n        CertDomain: \"node1.test.com\" # Domain to cert\n        CertFile: /etc/XrayR/cert/node1.test.com.cert # Provided if the CertMode is file\n        KeyFile: /etc/XrayR/cert/node1.test.com.key\n        Provider: alidns # DNS cert provider, Get the full support list here: https://go-acme.github.io/lego/dns/\n        Email: test@me.com\n        DNSEnv: # DNS ENV option used by DNS provider\n          ALICLOUD_ACCESS_KEY: aaa\n          ALICLOUD_SECRET_KEY: bbb\n        RealityConfig: # This config like RealityObject for xray-core, please check https://xtls.github.io/config/transport.html#realityobject\n          Dest: 80 # Same fallback dest\n          Xver: 0 # Same fallback xver\n          ServerNames:\n            - \"example.com\"\n            - \"www.example.com\"\n          PrivateKey: \"\" # Private key for server\n          MinClientVer: \"\" # Min client version\n          MaxClientVer: \"\" # Max client version\n          MaxTimeDiff: 0 # Max time difference, ms\n          ShortIds: # Short ids\n            - \"\"\n            - \"0123456789abcdef\"\n  # -\n  #   ApiConfig:\n  #     ApiHost: \"http://127.0.0.1:668\"\n  #     ApiKey: \"123\"\n  #     NodeID: 4\n  #     NodeType: Shadowsocks # Node type: V2ray, Shadowsocks, Trojan\n  #     Timeout: 30 # Timeout for the api request\n  #     EnableVless: false # Enable Vless for V2ray Type\n  #     EnableXTLS: false # Enable XTLS for V2ray and Trojan\n  #     SpeedLimit: 0 # Mbps, Local settings will replace remote settings\n  #     DeviceLimit: 0 # Local settings will replace remote settings\n  #   ControllerConfig:\n  #     ListenIP: 0.0.0.0 # IP address you want to listen\n  #     EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well\n  #     CertConfig:\n  #       CertMode: dns # Option about how to get certificate: none, file, http, dns\n  #       CertDomain: \"node1.test.com\" # Domain to cert\n  #       CertFile: /etc/XrayR/cert/node1.test.com.cert # Provided if the CertMode is file\n  #       KeyFile: /etc/XrayR/cert/node1.test.com.pem\n  #       Provider: alidns # DNS cert provider, Get the full support list here: https://go-acme.github.io/lego/dns/\n  #       Email: test@me.com\n  #       DNSEnv: # DNS ENV option used by DNS provider\n  #         ALICLOUD_ACCESS_KEY: aaa\n  #         ALICLOUD_SECRET_KEY: bbb\n\n"
  },
  {
    "path": "example/custom_inbound.json",
    "content": "[\n    {\n        \"listen\": \"0.0.0.0\",\n        \"port\": 1234,\n        \"protocol\": \"socks\",\n        \"settings\": {\n            \"auth\": \"noauth\",\n            \"accounts\": [\n                {\n                    \"user\": \"my-username\",\n                    \"pass\": \"my-password\"\n                }\n            ],\n            \"udp\": false,\n            \"ip\": \"127.0.0.1\",\n            \"userLevel\": 0\n        }\n    }\n]"
  },
  {
    "path": "example/custom_outbound.json",
    "content": "[\n    {\n        \"tag\": \"IPv4_out\",\n        \"protocol\": \"freedom\",\n        \"settings\": {}\n    },\n    {\n        \"tag\": \"IPv6_out\",\n        \"protocol\": \"freedom\",\n        \"settings\": {\n            \"domainStrategy\": \"UseIPv6\"\n        }\n    },\n    {\n        \"tag\": \"socks5-warp\",\n        \"protocol\": \"socks\",\n        \"settings\": {\n            \"servers\": [{\n                \"address\": \"127.0.0.1\",\n                \"port\": 40000\n            }]\n        }\n    },\n    {\n        \"protocol\": \"blackhole\",\n        \"tag\": \"block\"\n    }\n]"
  },
  {
    "path": "example/dns.json",
    "content": "{\n    \"servers\": [\n        \"1.1.1.1\",\n        \"8.8.8.8\",\n        \"localhost\"\n    ],\n    \"tag\": \"dns_inbound\"\n}"
  },
  {
    "path": "example/route.json",
    "content": "{\n    \"domainStrategy\": \"IPOnDemand\",\n    \"rules\": [\n        {\n            \"type\": \"field\",\n            \"outboundTag\": \"block\",\n            \"ip\": [\n                \"geoip:private\"\n            ]\n        },\n        {\n            \"type\": \"field\",\n            \"outboundTag\": \"block\",\n            \"protocol\": [\n                \"bittorrent\"\n            ]\n        },\n        {\n            \"type\": \"field\",\n            \"outboundTag\": \"socks5-warp\",\n            \"domain\": [\"\"]\n        },\n        {\n            \"type\": \"field\",\n            \"outboundTag\": \"IPv6_out\",\n            \"domain\": [\n                \"geosite:netflix\"\n            ]\n        },\n        {\n            \"type\": \"field\",\n            \"outboundTag\": \"IPv4_out\",\n            \"network\": \"udp,tcp\"\n        }\n    ]\n}"
  },
  {
    "path": "example/rulelist",
    "content": "(.+\\.|^)(360|so)\\.(cn|com)\nbaidu.com\ngoogle.com"
  },
  {
    "path": "go.mod",
    "content": "module github.com/Yuzuki616/V2bX\n\ngo 1.19\n\nrequire (\n\tgithub.com/Yuzuki616/hysteria/core v0.0.0-20230722103310-05508b7e5490\n\tgithub.com/Yuzuki616/quic-go v0.34.1\n\tgithub.com/beevik/ntp v1.2.0\n\tgithub.com/folbricht/routedns v0.1.20\n\tgithub.com/fsnotify/fsnotify v1.6.0\n\tgithub.com/go-acme/lego/v4 v4.13.3\n\tgithub.com/go-redis/redis/v8 v8.11.5\n\tgithub.com/go-resty/resty/v2 v2.7.0\n\tgithub.com/goccy/go-json v0.10.2\n\tgithub.com/hashicorp/go-multierror v1.1.1\n\tgithub.com/juju/ratelimit v1.0.2\n\tgithub.com/oschwald/geoip2-golang v1.9.0\n\tgithub.com/sirupsen/logrus v1.9.3\n\tgithub.com/spf13/cobra v1.7.0\n\tgithub.com/xtls/xray-core v1.8.3\n\tgolang.org/x/crypto v0.11.0\n\tgolang.org/x/sys v0.10.0\n\tgoogle.golang.org/protobuf v1.31.0\n\tgopkg.in/yaml.v3 v3.0.1\n)\n\nrequire (\n\tcloud.google.com/go/compute v1.19.1 // indirect\n\tcloud.google.com/go/compute/metadata v0.2.3 // indirect\n\tgithub.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect\n\tgithub.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect\n\tgithub.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect\n\tgithub.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect\n\tgithub.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect\n\tgithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0 // indirect\n\tgithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0 // indirect\n\tgithub.com/Azure/go-autorest v14.2.0+incompatible // indirect\n\tgithub.com/Azure/go-autorest/autorest v0.11.24 // indirect\n\tgithub.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect\n\tgithub.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect\n\tgithub.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect\n\tgithub.com/Azure/go-autorest/autorest/date v0.3.0 // indirect\n\tgithub.com/Azure/go-autorest/autorest/to v0.4.0 // indirect\n\tgithub.com/Azure/go-autorest/logger v0.2.1 // indirect\n\tgithub.com/Azure/go-autorest/tracing v0.6.0 // indirect\n\tgithub.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect\n\tgithub.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect\n\tgithub.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91 // indirect\n\tgithub.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect\n\tgithub.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect\n\tgithub.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect\n\tgithub.com/andybalholm/brotli v1.0.5 // indirect\n\tgithub.com/aws/aws-sdk-go v1.39.0 // indirect\n\tgithub.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect\n\tgithub.com/cenkalti/backoff/v4 v4.2.1 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.2.0 // indirect\n\tgithub.com/civo/civogo v0.3.11 // indirect\n\tgithub.com/cloudflare/cloudflare-go v0.70.0 // indirect\n\tgithub.com/coreos/go-iptables v0.6.0 // indirect\n\tgithub.com/cpu/goacmedns v0.1.1 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/deepmap/oapi-codegen v1.9.1 // indirect\n\tgithub.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect\n\tgithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect\n\tgithub.com/dimchansky/utfbom v1.1.1 // indirect\n\tgithub.com/dnsimple/dnsimple-go v1.2.0 // indirect\n\tgithub.com/exoscale/egoscale v0.100.1 // indirect\n\tgithub.com/fatih/structs v1.1.0 // indirect\n\tgithub.com/francoispqt/gojay v1.2.13 // indirect\n\tgithub.com/gaukas/godicttls v0.0.3 // indirect\n\tgithub.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // indirect\n\tgithub.com/go-errors/errors v1.0.1 // indirect\n\tgithub.com/go-jose/go-jose/v3 v3.0.0 // indirect\n\tgithub.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect\n\tgithub.com/golang-jwt/jwt/v4 v4.5.0 // indirect\n\tgithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect\n\tgithub.com/golang/mock v1.6.0 // indirect\n\tgithub.com/golang/protobuf v1.5.3 // indirect\n\tgithub.com/google/btree v1.1.2 // indirect\n\tgithub.com/google/go-querystring v1.1.0 // indirect\n\tgithub.com/google/gopacket v1.1.19 // indirect\n\tgithub.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect\n\tgithub.com/google/uuid v1.3.0 // indirect\n\tgithub.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect\n\tgithub.com/googleapis/gax-go/v2 v2.7.1 // indirect\n\tgithub.com/gophercloud/gophercloud v1.0.0 // indirect\n\tgithub.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect\n\tgithub.com/gorilla/websocket v1.5.0 // indirect\n\tgithub.com/hashicorp/errwrap v1.0.0 // indirect\n\tgithub.com/hashicorp/go-cleanhttp v0.5.2 // indirect\n\tgithub.com/hashicorp/go-retryablehttp v0.7.4 // indirect\n\tgithub.com/hashicorp/golang-lru/v2 v2.0.1 // indirect\n\tgithub.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/infobloxopen/infoblox-go-client v1.1.1 // indirect\n\tgithub.com/jmespath/go-jmespath v0.4.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/jtacoma/uritemplates v1.0.0 // indirect\n\tgithub.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect\n\tgithub.com/klauspost/compress v1.16.6 // indirect\n\tgithub.com/klauspost/cpuid/v2 v2.2.5 // indirect\n\tgithub.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect\n\tgithub.com/kylelemons/godebug v1.1.0 // indirect\n\tgithub.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect\n\tgithub.com/labbsr0x/goh v1.0.1 // indirect\n\tgithub.com/linode/linodego v1.17.2 // indirect\n\tgithub.com/liquidweb/go-lwApi v0.0.5 // indirect\n\tgithub.com/liquidweb/liquidweb-cli v0.6.9 // indirect\n\tgithub.com/liquidweb/liquidweb-go v1.6.3 // indirect\n\tgithub.com/lucas-clemente/quic-go v0.31.1 // indirect\n\tgithub.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect\n\tgithub.com/marten-seemann/qpack v0.3.0 // indirect\n\tgithub.com/marten-seemann/qtls-go1-18 v0.1.4 // indirect\n\tgithub.com/marten-seemann/qtls-go1-19 v0.1.2 // indirect\n\tgithub.com/mattn/go-isatty v0.0.19 // indirect\n\tgithub.com/miekg/dns v1.1.55 // indirect\n\tgithub.com/mimuret/golang-iij-dpf v0.9.1 // indirect\n\tgithub.com/mitchellh/go-homedir v1.1.0 // indirect\n\tgithub.com/mitchellh/mapstructure v1.5.0 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect\n\tgithub.com/nrdcg/auroradns v1.1.0 // indirect\n\tgithub.com/nrdcg/desec v0.7.0 // indirect\n\tgithub.com/nrdcg/dnspod-go v0.4.0 // indirect\n\tgithub.com/nrdcg/freemyip v0.2.0 // indirect\n\tgithub.com/nrdcg/goinwx v0.8.2 // indirect\n\tgithub.com/nrdcg/namesilo v0.2.1 // indirect\n\tgithub.com/nrdcg/nodion v0.1.0 // indirect\n\tgithub.com/nrdcg/porkbun v0.2.0 // indirect\n\tgithub.com/nzdjb/go-metaname v1.0.0 // indirect\n\tgithub.com/onsi/ginkgo/v2 v2.11.0 // indirect\n\tgithub.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect\n\tgithub.com/oschwald/maxminddb-golang v1.11.0 // indirect\n\tgithub.com/ovh/go-ovh v1.4.1 // indirect\n\tgithub.com/patrickmn/go-cache v2.1.0+incompatible // indirect\n\tgithub.com/pion/dtls/v2 v2.2.4 // indirect\n\tgithub.com/pion/logging v0.2.2 // indirect\n\tgithub.com/pion/transport/v2 v2.0.0 // indirect\n\tgithub.com/pion/udp v0.1.4 // indirect\n\tgithub.com/pires/go-proxyproto v0.7.0 // indirect\n\tgithub.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/pquerna/otp v1.4.0 // indirect\n\tgithub.com/quic-go/qtls-go1-19 v0.3.2 // indirect\n\tgithub.com/quic-go/qtls-go1-20 v0.2.2 // indirect\n\tgithub.com/quic-go/quic-go v0.35.1 // indirect\n\tgithub.com/refraction-networking/utls v1.3.2 // indirect\n\tgithub.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect\n\tgithub.com/sacloud/api-client-go v0.2.8 // indirect\n\tgithub.com/sacloud/go-http v0.1.6 // indirect\n\tgithub.com/sacloud/iaas-api-go v1.11.1 // indirect\n\tgithub.com/sacloud/packages-go v0.0.9 // indirect\n\tgithub.com/sagernet/sing v0.2.5 // indirect\n\tgithub.com/sagernet/sing-shadowsocks v0.2.2 // indirect\n\tgithub.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect\n\tgithub.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 // indirect\n\tgithub.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect\n\tgithub.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 // indirect\n\tgithub.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect\n\tgithub.com/softlayer/softlayer-go v1.1.2 // indirect\n\tgithub.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect\n\tgithub.com/spf13/cast v1.3.1 // indirect\n\tgithub.com/spf13/pflag v1.0.5 // indirect\n\tgithub.com/stretchr/objx v0.5.0 // indirect\n\tgithub.com/stretchr/testify v1.8.4 // indirect\n\tgithub.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect\n\tgithub.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect\n\tgithub.com/transip/gotransip/v6 v6.20.0 // indirect\n\tgithub.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect\n\tgithub.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a // indirect\n\tgithub.com/txthinking/x v0.0.0-20210326105829-476fab902fbe // indirect\n\tgithub.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c // indirect\n\tgithub.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect\n\tgithub.com/vinyldns/go-vinyldns v0.9.16 // indirect\n\tgithub.com/vultr/govultr/v2 v2.17.2 // indirect\n\tgithub.com/xtls/reality v0.0.0-20230613075828-e07c3b04b983 // indirect\n\tgithub.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f // indirect\n\tgithub.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect\n\tgo.opencensus.io v0.24.0 // indirect\n\tgo.uber.org/atomic v1.11.0 // indirect\n\tgo.uber.org/ratelimit v0.2.0 // indirect\n\tgolang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect\n\tgolang.org/x/mod v0.11.0 // indirect\n\tgolang.org/x/net v0.11.0 // indirect\n\tgolang.org/x/oauth2 v0.9.0 // indirect\n\tgolang.org/x/text v0.11.0 // indirect\n\tgolang.org/x/time v0.3.0 // indirect\n\tgolang.org/x/tools v0.10.0 // indirect\n\tgoogle.golang.org/api v0.114.0 // indirect\n\tgoogle.golang.org/appengine v1.6.7 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect\n\tgoogle.golang.org/grpc v1.56.0 // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgopkg.in/ns1/ns1-go.v2 v2.7.6 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect\n\tlukechampine.com/blake3 v1.2.1 // indirect\n)\n\nexclude gvisor.dev/gvisor v0.0.0-20230313184804-9bf6dd27710d\n"
  },
  {
    "path": "go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY=\ncloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=\ncloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=\ncloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ndmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ndmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=\ndmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=\ndmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=\ngit.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=\ngithub.com/AdamSLevy/jsonrpc2/v14 v14.1.0 h1:Dy3M9aegiI7d7PF1LUdjbVigJReo+QOceYsMyFh9qoE=\ngithub.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesnDfrtF6RFUGzQfLo=\ngithub.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=\ngithub.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=\ngithub.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 h1:8kDqDngH+DmVBiCtIjCFTGa7MBnsIOkF9IccInFEbjk=\ngithub.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=\ngithub.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=\ngithub.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0 h1:8iR6OLffWWorFdzL2JFCab5xpD8VKEE2DUBBl+HNTDY=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0/go.mod h1:copqlcjMWc/wgQ1N2fzsJFQxDdqKGg1EQt8T5wJMOGE=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0 h1:rR8ZW79lE/ppfXTfiYSnMFv5EzmVuY4pfZWIkscIJ64=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0/go.mod h1:y2zXtLSMM/X5Mfawq0lOftpWn3f4V6OCsRdINsvWBPI=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8=\ngithub.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=\ngithub.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=\ngithub.com/Azure/go-autorest/autorest v0.11.24 h1:1fIGgHKqVm54KIPT+q8Zmd1QlVsmHqeUGso5qm2BqqE=\ngithub.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=\ngithub.com/Azure/go-autorest/autorest/azure/auth v0.5.12 h1:wkAZRgT/pn8HhFyzfe9UnqOjJYqlembgCTi72Bm/xKk=\ngithub.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg=\ngithub.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4=\ngithub.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg=\ngithub.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=\ngithub.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=\ngithub.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=\ngithub.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=\ngithub.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=\ngithub.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=\ngithub.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=\ngithub.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=\ngithub.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY=\ngithub.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24=\ngithub.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=\ngithub.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91 h1:vX+gnvBc56EbWYrmlhYbFYRaeikAke1GL84N4BEYOFE=\ngithub.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91/go.mod h1:cDLGBht23g0XQdLjzn6xOGXDkLK182YfINAaZEQLCHQ=\ngithub.com/Yuzuki616/hysteria/core v0.0.0-20230722103310-05508b7e5490 h1:OqS0ywNFR0fEWteLyTcexUNkpdb3vTl5EdMn9gMJMCc=\ngithub.com/Yuzuki616/hysteria/core v0.0.0-20230722103310-05508b7e5490/go.mod h1:Byg39a10tXQ6ysRz5r59YhVMUKgXAThI+0/LcVr+WOE=\ngithub.com/Yuzuki616/quic-go v0.34.1 h1:9Is+Dofzn6qJ9a9t4Ixe4oX7Cb4LU7u4zDozVB8DBDY=\ngithub.com/Yuzuki616/quic-go v0.34.1/go.mod h1:089qZpsXn7CL8kE7G2HurF0bLiZnzQdRIrT7PM0MMQs=\ngithub.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 h1:F1j7z+/DKEsYqZNoxC6wvfmaiDneLsQOFQmuq9NADSY=\ngithub.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2/go.mod h1:QlXr/TrICfQ/ANa76sLeQyhAJyNR9sEcfNuZBkY9jgY=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 h1:J45/QHgrzUdqe/Vco/Vxk0wRvdS2nKUxmf/zLgvfass=\ngithub.com/aliyun/alibaba-cloud-sdk-go v1.61.1755/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=\ngithub.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI=\ngithub.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=\ngithub.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=\ngithub.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=\ngithub.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/aws/aws-sdk-go v1.39.0 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo=\ngithub.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=\ngithub.com/beevik/ntp v1.2.0 h1:n1teVGbd4YM36FlGvWYfccBIdGzeaakHrTlo6RSL8mw=\ngithub.com/beevik/ntp v1.2.0/go.mod h1:vD6h1um4kzXpqmLTuu0cCLcC+NfvC0IC+ltmEDA8E78=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=\ngithub.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=\ngithub.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=\ngithub.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=\ngithub.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw=\ngithub.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=\ngithub.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=\ngithub.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=\ngithub.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/civo/civogo v0.3.11 h1:mON/fyrV946Sbk6paRtOSGsN+asCgCmHCgArf5xmGxM=\ngithub.com/civo/civogo v0.3.11/go.mod h1:7+GeeFwc4AYTULaEshpT2vIcl3Qq8HPoxA17viX3l6g=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cloudflare/cloudflare-go v0.70.0 h1:4opGbUygM8DjirUuaz23jn3akuAcnOCEx+0nQtQEcFo=\ngithub.com/cloudflare/cloudflare-go v0.70.0/go.mod h1:VW6GuazkaZ4xEDkFt24lkXQUsE8q7BiGqDniC2s8WEM=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=\ngithub.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4=\ngithub.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=\ngithub.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=\ngithub.com/deepmap/oapi-codegen v1.9.1 h1:yHmEnA7jSTUMQgV+uN02WpZtwHnz2CBW3mZRIxr1vtI=\ngithub.com/deepmap/oapi-codegen v1.9.1/go.mod h1:PLqNAhdedP8ttRpBBkzLKU3bp+Fpy+tTgeAMlztR2cw=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=\ngithub.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=\ngithub.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=\ngithub.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=\ngithub.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=\ngithub.com/dnsimple/dnsimple-go v1.2.0 h1:ddTGyLVKly5HKb5L65AkLqFqwZlWo3WnR0BlFZlIddM=\ngithub.com/dnsimple/dnsimple-go v1.2.0/go.mod h1:z/cs26v/eiRvUyXsHQBLd8lWF8+cD6GbmkPH84plM4U=\ngithub.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=\ngithub.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/exoscale/egoscale v0.100.1 h1:iXsV1Ei7daqe/6FYSCSDyrFs1iUG1l1X9qNh2uMw6z0=\ngithub.com/exoscale/egoscale v0.100.1/go.mod h1:BAb9p4rmyU+Wl400CJZO5270H2sXtdsZjLcm5xMKkz4=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=\ngithub.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=\ngithub.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=\ngithub.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=\ngithub.com/folbricht/routedns v0.1.20 h1:OCbHLzgcctbp+GRE4QVfnZYd1du3G0muO/Ba9o4QCsw=\ngithub.com/folbricht/routedns v0.1.20/go.mod h1:D8Vd2zJKkIQncvKTBJyed2poKqgpWKFhwIFGdWxnmCQ=\ngithub.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=\ngithub.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=\ngithub.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=\ngithub.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=\ngithub.com/gaukas/godicttls v0.0.3 h1:YNDIf0d9adcxOijiLrEzpfZGAkNwLRzPaG6OjU7EITk=\ngithub.com/gaukas/godicttls v0.0.3/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=\ngithub.com/getkin/kin-openapi v0.87.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=\ngithub.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=\ngithub.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=\ngithub.com/go-acme/lego/v4 v4.13.3 h1:aZ1S9FXIkCWG3Uw/rZKSD+MOuO8ZB1t6p9VCg6jJiNY=\ngithub.com/go-acme/lego/v4 v4.13.3/go.mod h1:c/iodVGMeBXG/+KiQczoNkySo3YLWTVa0kiyeVd/FHc=\ngithub.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=\ngithub.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=\ngithub.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=\ngithub.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=\ngithub.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=\ngithub.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=\ngithub.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=\ngithub.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=\ngithub.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=\ngithub.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=\ngithub.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=\ngithub.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=\ngithub.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=\ngithub.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=\ngithub.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA=\ngithub.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=\ngithub.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=\ngithub.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=\ngithub.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=\ngithub.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=\ngithub.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=\ngithub.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=\ngithub.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=\ngithub.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=\ngithub.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=\ngithub.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=\ngithub.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=\ngithub.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=\ngithub.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=\ngithub.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=\ngithub.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=\ngithub.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=\ngithub.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs=\ngithub.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=\ngithub.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=\ngithub.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=\ngithub.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=\ngithub.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A=\ngithub.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=\ngithub.com/gophercloud/gophercloud v0.15.1-0.20210202035223-633d73521055/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4=\ngithub.com/gophercloud/gophercloud v1.0.0 h1:9nTGx0jizmHxDobe4mck89FyQHVyA3CaXLIUSGJjP9k=\ngithub.com/gophercloud/gophercloud v1.0.0/go.mod h1:Q8fZtyi5zZxPS/j9aj3sSxtvj41AdQMDwyo1myduD5c=\ngithub.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae h1:Hi3IgB9RQDE15Kfovd8MTZrcana+UlQqNbOif8dLpA0=\ngithub.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae/go.mod h1:wx8HMD8oQD0Ryhz6+6ykq75PJ79iPyEqYHfwZ4l7OsA=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=\ngithub.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=\ngithub.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=\ngithub.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=\ngithub.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=\ngithub.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=\ngithub.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=\ngithub.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=\ngithub.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA=\ngithub.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4=\ngithub.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df h1:MZf03xP9WdakyXhOWuAD5uPK3wHh96wCsqe3hCMKh8E=\ngithub.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/infobloxopen/infoblox-go-client v1.1.1 h1:728A6LbLjptj/7kZjHyIxQnm768PWHfGFm0HH8FnbtU=\ngithub.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI=\ngithub.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=\ngithub.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=\ngithub.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=\ngithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=\ngithub.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=\ngithub.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=\ngithub.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=\ngithub.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jtacoma/uritemplates v1.0.0 h1:xwx5sBF7pPAb0Uj8lDC1Q/aBPpOFyQza7OC705ZlLCo=\ngithub.com/jtacoma/uritemplates v1.0.0/go.mod h1:IhIICdE9OcvgUnGwTtJxgBQ+VrTrti5PcbLVSJianO8=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=\ngithub.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg=\ngithub.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/klauspost/compress v1.16.6 h1:91SKEy4K37vkp255cJ8QesJhjyRO0hn9i9G0GoUwLsk=\ngithub.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=\ngithub.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=\ngithub.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=\ngithub.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00=\ngithub.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\ngithub.com/labbsr0x/bindman-dns-webhook v1.0.2 h1:I7ITbmQPAVwrDdhd6dHKi+MYJTJqPCK0jE6YNBAevnk=\ngithub.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA=\ngithub.com/labbsr0x/goh v1.0.1 h1:97aBJkDjpyBZGPbQuOK5/gHcSFbcr5aRsq3RSRJFpPk=\ngithub.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=\ngithub.com/labstack/echo/v4 v4.6.3/go.mod h1:Hk5OiHj0kDqmFq7aHe7eDqI7CUhuCrfpupQtLGGLm7A=\ngithub.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=\ngithub.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=\ngithub.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ=\ngithub.com/lestrrat-go/codegen v1.0.2/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM=\ngithub.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE=\ngithub.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=\ngithub.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA=\ngithub.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=\ngithub.com/linode/linodego v1.17.2 h1:b32dj4662PGG5P9qVa6nBezccWdqgukndlMIuPGq1CQ=\ngithub.com/linode/linodego v1.17.2/go.mod h1:C2iyT3Vg2O2sPxkWka4XAQ5WSUtm5LmTZ3Adw43Ra7Q=\ngithub.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=\ngithub.com/liquidweb/go-lwApi v0.0.5 h1:CT4cdXzJXmo0bon298kS7NeSk+Gt8/UHpWBBol1NGCA=\ngithub.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=\ngithub.com/liquidweb/liquidweb-cli v0.6.9 h1:acbIvdRauiwbxIsOCEMXGwF75aSJDbDiyAWPjVnwoYM=\ngithub.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ=\ngithub.com/liquidweb/liquidweb-go v1.6.3 h1:NVHvcnX3eb3BltiIoA+gLYn15nOpkYkdizOEYGSKrk4=\ngithub.com/liquidweb/liquidweb-go v1.6.3/go.mod h1:SuXXp+thr28LnjEw18AYtWwIbWMHSUiajPQs8T9c/Rc=\ngithub.com/lucas-clemente/quic-go v0.31.1 h1:O8Od7hfioqq0PMYHDyBkxU2aA7iZ2W9pjbrWuja2YR4=\ngithub.com/lucas-clemente/quic-go v0.31.1/go.mod h1:0wFbizLgYzqHqtlyxyCaJKlE7bYgE6JQ+54TLd/Dq2g=\ngithub.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=\ngithub.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=\ngithub.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=\ngithub.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE=\ngithub.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g=\ngithub.com/marten-seemann/qtls-go1-18 v0.1.4 h1:ogomB+lWV3Vmwiu6RTwDVTMGx+9j7SEi98e8QB35Its=\ngithub.com/marten-seemann/qtls-go1-18 v0.1.4/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=\ngithub.com/marten-seemann/qtls-go1-19 v0.1.2 h1:ZevAEqKXH0bZmoOBPiqX2h5rhQ7cbZi+X+rlq2JUbCE=\ngithub.com/marten-seemann/qtls-go1-19 v0.1.2/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=\ngithub.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=\ngithub.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=\ngithub.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=\ngithub.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=\ngithub.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=\ngithub.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=\ngithub.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=\ngithub.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=\ngithub.com/mimuret/golang-iij-dpf v0.9.1 h1:Gj6EhHJkOhr+q2RnvRPJsPMcjuVnWPSccEHyoEehU34=\ngithub.com/mimuret/golang-iij-dpf v0.9.1/go.mod h1:sl9KyOkESib9+KRD3HaGpgi1xk7eoN2+d96LCLsME2M=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=\ngithub.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g=\ngithub.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=\ngithub.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=\ngithub.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=\ngithub.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/nrdcg/auroradns v1.1.0 h1:KekGh8kmf2MNwqZVVYo/fw/ZONt8QMEmbMFOeljteWo=\ngithub.com/nrdcg/auroradns v1.1.0/go.mod h1:O7tViUZbAcnykVnrGkXzIJTHoQCHcgalgAe6X1mzHfk=\ngithub.com/nrdcg/desec v0.7.0 h1:iuGhi4pstF3+vJWwt292Oqe2+AsSPKDynQna/eu1fDs=\ngithub.com/nrdcg/desec v0.7.0/go.mod h1:e1uRqqKv1mJdd5+SQROAhmy75lKMphLzWIuASLkpeFY=\ngithub.com/nrdcg/dnspod-go v0.4.0 h1:c/jn1mLZNKF3/osJ6mz3QPxTudvPArXTjpkmYj0uK6U=\ngithub.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ=\ngithub.com/nrdcg/freemyip v0.2.0 h1:/GscavT4GVqAY13HExl5UyoB4wlchv6Cg5NYDGsUoJ8=\ngithub.com/nrdcg/freemyip v0.2.0/go.mod h1:HjF0Yz0lSb37HD2ihIyGz9esyGcxbCrrGFLPpKevbx4=\ngithub.com/nrdcg/goinwx v0.8.2 h1:RmjiHlEA+lzi3toXyPSaE6hWnBQ0+G+1u7w8C6Fpp4g=\ngithub.com/nrdcg/goinwx v0.8.2/go.mod h1:mnMSTi7CXBu2io4DzdOBoGFA1XclD0sEPWJaDhNgkA4=\ngithub.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg=\ngithub.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=\ngithub.com/nrdcg/nodion v0.1.0 h1:zLKaqTn2X0aDuBHHfyA1zFgeZfiCpmu/O9DM73okavw=\ngithub.com/nrdcg/nodion v0.1.0/go.mod h1:inbuh3neCtIWlMPZHtEpe43TmRXxHV6+hk97iCZicms=\ngithub.com/nrdcg/porkbun v0.2.0 h1:ghaqPtIKcffba99epWFkK3VWf6TKJT9WMXMgaTqv95Y=\ngithub.com/nrdcg/porkbun v0.2.0/go.mod h1:i0uLMn9ItFsLsSQIAeEu1wQ9/+6EvX1eQw15hulMMRw=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=\ngithub.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=\ngithub.com/nzdjb/go-metaname v1.0.0 h1:sNASlZC1RM3nSudtBTE1a3ZVTDyTpjqI5WXRPrdZ9Hg=\ngithub.com/nzdjb/go-metaname v1.0.0/go.mod h1:0GR0LshZax1Lz4VrOrfNSE4dGvTp7HGjiemdczXT2H4=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=\ngithub.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=\ngithub.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=\ngithub.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=\ngithub.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=\ngithub.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=\ngithub.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=\ngithub.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=\ngithub.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=\ngithub.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=\ngithub.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU=\ngithub.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=\ngithub.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc=\ngithub.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y=\ngithub.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0=\ngithub.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg=\ngithub.com/ovh/go-ovh v1.4.1 h1:VBGa5wMyQtTP7Zb+w97zRCh9sLtM/2YKRyy+MEJmWaM=\ngithub.com/ovh/go-ovh v1.4.1/go.mod h1:6bL6pPyUT7tBfI0pqOegJgRjgjuO+mOo+MyXd1EEC0M=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=\ngithub.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=\ngithub.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=\ngithub.com/pion/dtls/v2 v2.2.4 h1:YSfYwDQgrxMYXLBc/m7PFY5BVtWlNm/DN4qoU2CbcWg=\ngithub.com/pion/dtls/v2 v2.2.4/go.mod h1:WGKfxqhrddne4Kg3p11FUMJrynkOY4lb25zHNO49wuw=\ngithub.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=\ngithub.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=\ngithub.com/pion/transport/v2 v2.0.0 h1:bsMYyqHCbkvHwj+eNCFBuxtlKndKfyGI2vaQmM3fIE4=\ngithub.com/pion/transport/v2 v2.0.0/go.mod h1:HS2MEBJTwD+1ZI2eSXSvHJx/HnzQqRy2/LXxt6eVMHc=\ngithub.com/pion/udp v0.1.4 h1:OowsTmu1Od3sD6i3fQUJxJn2fEvJO6L1TidgadtbTI8=\ngithub.com/pion/udp v0.1.4/go.mod h1:G8LDo56HsFwC24LIcnT4YIDU5qcB6NepqqjP0keL2us=\ngithub.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=\ngithub.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=\ngithub.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=\ngithub.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=\ngithub.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=\ngithub.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=\ngithub.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=\ngithub.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=\ngithub.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=\ngithub.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=\ngithub.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=\ngithub.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=\ngithub.com/quic-go/quic-go v0.35.1 h1:b0kzj6b/cQAf05cT0CkQubHM31wiA+xH3IBkxP62poo=\ngithub.com/quic-go/quic-go v0.35.1/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g=\ngithub.com/refraction-networking/utls v1.3.2 h1:o+AkWB57mkcoW36ET7uJ002CpBWHu0KPxi6vzxvPnv8=\ngithub.com/refraction-networking/utls v1.3.2/go.mod h1:fmoaOww2bxzzEpIKOebIsnBvjQpqP7L2vcm/9KUfm/E=\ngithub.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=\ngithub.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=\ngithub.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=\ngithub.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sacloud/api-client-go v0.2.8 h1:tIY6PZNBX900K66TqEPa4d6UIbedUczfCBnPJkzi8kw=\ngithub.com/sacloud/api-client-go v0.2.8/go.mod h1:0CV/kWNYlS1hCNdnk6Wx7Wdg8DPFCnv0zOIzdXjeAeY=\ngithub.com/sacloud/go-http v0.1.6 h1:lJGXDt9xrxJiDszRPaN9NIP8MVj10YKMzmnyzdSfI8w=\ngithub.com/sacloud/go-http v0.1.6/go.mod h1:oLAHoDJRkptf8sq4fE8oERLkdCh0kJWfWu+paoJY7I0=\ngithub.com/sacloud/iaas-api-go v1.11.1 h1:2MsFZ4H1uRdRVx2nVXuERWQ3swoFc3XreIV5hJ3Nsws=\ngithub.com/sacloud/iaas-api-go v1.11.1/go.mod h1:uBDSa06F/V0OnoR66jGdbH0PVnCJw+NeE9RVbVgMfss=\ngithub.com/sacloud/packages-go v0.0.9 h1:GbinkBLC/eirFhHpLjoDW6JV7+95Rnd2d8RWj7Afeks=\ngithub.com/sacloud/packages-go v0.0.9/go.mod h1:k+EEUMF2LlncjbNIJNOqLyZ9wjTESPIWIk1OA7x9j2Q=\ngithub.com/sagernet/sing v0.2.5 h1:N8sUluR8GZvR9DqUiH3FA3vBb4m/EDdOVTYUrDzJvmY=\ngithub.com/sagernet/sing v0.2.5/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=\ngithub.com/sagernet/sing-shadowsocks v0.2.2 h1:ezSdVhrmIcwDXmCZF3bOJVMuVtTQWpda+1Op+Ie2TA4=\ngithub.com/sagernet/sing-shadowsocks v0.2.2/go.mod h1:JIBWG6a7orB2HxBxYElViQFLUQxFVG7DuqIj8gD7uCQ=\ngithub.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=\ngithub.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=\ngithub.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 h1:1WuWJu7/e8SqK+uQl7lfk/N/oMZTL2NE/TJsNKRNMc4=\ngithub.com/scaleway/scaleway-sdk-go v1.0.0-beta.17/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=\ngithub.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=\ngithub.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=\ngithub.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=\ngithub.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=\ngithub.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=\ngithub.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=\ngithub.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=\ngithub.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=\ngithub.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=\ngithub.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=\ngithub.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=\ngithub.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=\ngithub.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=\ngithub.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=\ngithub.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=\ngithub.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=\ngithub.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=\ngithub.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=\ngithub.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=\ngithub.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=\ngithub.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=\ngithub.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 h1:ZTzdx88+AcnjqUfJwnz89UBrMSBQ1NEysg9u5d+dU9c=\ngithub.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04/go.mod h1:5KS21fpch8TIMyAUv/qQqTa3GZfBDYgjaZbd2KXKYfg=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=\ngithub.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=\ngithub.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 h1:hp2CYQUINdZMHdvTdXtPOY2ainKl4IoMcpAXEf2xj3Q=\ngithub.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/smartystreets/gunit v1.0.4 h1:tpTjnuH7MLlqhoD21vRoMZbMIi5GmBsAJDFyF67GhZA=\ngithub.com/softlayer/softlayer-go v1.1.2 h1:rUSSGCyaxymvTOsaFjwr+cGxA8muw3xg2LSrIMNcN/c=\ngithub.com/softlayer/softlayer-go v1.1.2/go.mod h1:hvAbzGH4LRXA6yXY8BNx99yoqZ7urfDdtl9mvBf0G+g=\ngithub.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e h1:3OgWYFw7jxCZPcvAg+4R8A50GZ+CCkARF10lxu2qDsQ=\ngithub.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e/go.mod h1:fKZCUVdirrxrBpwd9wb+lSoVixvpwAu8eHzbQB2tums=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=\ngithub.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=\ngithub.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=\ngithub.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=\ngithub.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=\ngithub.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 h1:mmz27tVi2r70JYnm5y0Zk8w0Qzsx+vfUw3oqSyrEfP8=\ngithub.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=\ngithub.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 h1:g9SWTaTy/rEuhMErC2jWq9Qt5ci+jBYSvXnJsLq4adg=\ngithub.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490/go.mod h1:l9q4vc1QiawUB1m3RU+87yLvrrxe54jc0w/kEl4DbSQ=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/transip/gotransip/v6 v6.20.0 h1:AuvwyOZ51f2brzMbTqlRy/wmaM3kF7Vx5Wds8xcDflY=\ngithub.com/transip/gotransip/v6 v6.20.0/go.mod h1:nzv9eN2tdsUrm5nG5ZX6AugYIU4qgsMwIn2c0EZLk8c=\ngithub.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0=\ngithub.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf/go.mod h1:CLUSJbazqETbaR+i0YAhXBICV9TrKH93pziccMhmhpM=\ngithub.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a h1:BOqgJ4jku0LHPDoR51RD8Mxmo0LHxCzJT/M9MemYdHo=\ngithub.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a/go.mod h1:7NloQcrxaZYKURWph5HLxVDlIwMHJXCPkeWPtpftsIg=\ngithub.com/txthinking/x v0.0.0-20210326105829-476fab902fbe h1:gMWxZxBFRAXqoGkwkYlPX2zvyyKNWJpxOxCrjqJkm5A=\ngithub.com/txthinking/x v0.0.0-20210326105829-476fab902fbe/go.mod h1:WgqbSEmUYSjEV3B1qmee/PpP2NYEz4bL9/+mF1ma+s4=\ngithub.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=\ngithub.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=\ngithub.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=\ngithub.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=\ngithub.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c h1:mKnW6IGLw7uXu6DL6RitufZWcXS6hCnauXRUFof7rKM=\ngithub.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c/go.mod h1:F4UyVEmq4/m5lAmx+GccrxyRCXmnBjzUL09JLTQFp94=\ngithub.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=\ngithub.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=\ngithub.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=\ngithub.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=\ngithub.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=\ngithub.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=\ngithub.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ=\ngithub.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q=\ngithub.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=\ngithub.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngithub.com/xtls/reality v0.0.0-20230613075828-e07c3b04b983 h1:AMyzgjkh54WocjQSlCnT1LhDc/BKiUqtNOv40AkpURs=\ngithub.com/xtls/reality v0.0.0-20230613075828-e07c3b04b983/go.mod h1:rkuAY1S9F8eI8gDiPDYvACE8e2uwkyg8qoOTuwWov7Y=\ngithub.com/xtls/xray-core v1.8.3 h1:lxaVklPjLKqUU4ua4qH8SBaRcAaNHlH+LmXOx0U/Ejg=\ngithub.com/xtls/xray-core v1.8.3/go.mod h1:i7t4JFnq828P2+XK0XjGQ8W9x78iu+EJ7jI4l3sonIw=\ngithub.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f h1:cG+ehPRJSlqljSufLf1KXeXpUd1dLNjnzA18mZcB/O0=\ngithub.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=\ngithub.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 h1:2wzke3JH7OtN20WsNDZx2VH/TCmsbqtDEbXzjF+i05E=\ngithub.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997/go.mod h1:2CHKs/YGbCcNn/BPaCkEBwKz/FNCELi+MLILjR9RaTA=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=\ngo.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=\ngo.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA=\ngo.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=\ngolang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=\ngolang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=\ngolang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=\ngolang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=\ngolang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=\ngolang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=\ngolang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=\ngolang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=\ngolang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=\ngolang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=\ngolang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.9.0 h1:BPpt2kU7oMRq3kCHAA1tbSEshXRw1LpG2ztgDwrzuAs=\ngolang.org/x/oauth2 v0.9.0/go.mod h1:qYgFZaFiu6Wg24azG8bdV52QJXJGbZzIIsRCdVKzbLw=\ngolang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=\ngolang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=\ngolang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=\ngolang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=\ngolang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=\ngolang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=\ngolang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=\ngolang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=\ngolang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=\ngolang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=\ngolang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=\ngolang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=\ngolang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=\ngolang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=\ngoogle.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=\ngoogle.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE=\ngoogle.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=\ngoogle.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=\ngoogle.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=\ngoogle.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=\ngoogle.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=\ngoogle.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=\ngoogle.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=\ngoogle.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=\ngoogle.golang.org/grpc v1.56.0 h1:+y7Bs8rtMd07LeXmL3NxcTLn7mUkbKZqEpPhMNkwJEE=\ngoogle.golang.org/grpc v1.56.0/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=\ngoogle.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=\ngopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ns1/ns1-go.v2 v2.7.6 h1:mCPl7q0jbIGACXvGBljAuuApmKZo3rRi4tlRIEbMvjA=\ngopkg.in/ns1/ns1-go.v2 v2.7.6/go.mod h1:GMnKY+ZuoJ+lVLL+78uSTjwTz2jMazq6AfGKQOYhsPk=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngrpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=\ngvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=\ngvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=\nhonnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nlukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=\nlukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nsourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=\nsourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=\n"
  },
  {
    "path": "limiter/clear.go",
    "content": "package limiter\n\nimport log \"github.com/sirupsen/logrus\"\n\nfunc ClearOnlineIP() error {\n\tlog.WithField(\"Type\", \"Limiter\").\n\t\tDebug(\"Clear online ip...\")\n\tlimitLock.RLock()\n\tfor _, l := range limiter {\n\t\tl.ConnLimiter.ClearOnlineIP()\n\t}\n\tlimitLock.RUnlock()\n\tlog.WithField(\"Type\", \"Limiter\").\n\t\tDebug(\"Clear online ip done\")\n\treturn nil\n}\n"
  },
  {
    "path": "limiter/conn.go",
    "content": "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\tcount     sync.Map // map[string]int\n\tip        sync.Map // map[string]map[string]int\n}\n\nfunc NewConnLimiter(conn int, ip int, realtime bool) *ConnLimiter {\n\treturn &ConnLimiter{\n\t\trealtime:  realtime,\n\t\tconnLimit: conn,\n\t\tipLimit:   ip,\n\t\tcount:     sync.Map{},\n\t\tip:        sync.Map{},\n\t}\n}\n\nfunc (c *ConnLimiter) AddConnCount(user string, ip string, isTcp bool) (limit bool) {\n\tif c.connLimit != 0 {\n\t\tif v, ok := c.count.Load(user); ok {\n\t\t\tif v.(int) >= c.connLimit {\n\t\t\t\t// over connection limit\n\t\t\t\treturn true\n\t\t\t} else if isTcp {\n\t\t\t\t// tcp protocol\n\t\t\t\t// connection count add\n\t\t\t\tc.count.Store(user, v.(int)+1)\n\t\t\t}\n\t\t} else if isTcp {\n\t\t\t// tcp protocol\n\t\t\t// store connection count\n\t\t\tc.count.Store(user, 1)\n\t\t}\n\t}\n\tif c.ipLimit == 0 {\n\t\treturn false\n\t}\n\t// first user map\n\tipMap := new(sync.Map)\n\tif c.realtime {\n\t\tif isTcp {\n\t\t\tipMap.Store(ip, 2)\n\t\t} else {\n\t\t\tipMap.Store(ip, 1)\n\t\t}\n\t} else {\n\t\tipMap.Store(ip, time.Now())\n\t}\n\t// check user online ip\n\tif v, ok := c.ip.LoadOrStore(user, ipMap); ok {\n\t\t// have user\n\t\tips := v.(*sync.Map)\n\t\tcn := 0\n\t\tif online, ok := ips.Load(ip); ok {\n\t\t\t// online ip\n\t\t\tif c.realtime {\n\t\t\t\tif isTcp {\n\t\t\t\t\t// tcp count add\n\t\t\t\t\tips.Store(ip, online.(int)+2)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// update connect time for not realtime\n\t\t\t\tips.Store(ip, time.Now())\n\t\t\t}\n\t\t} else {\n\t\t\t// not online ip\n\t\t\tips.Range(func(_, _ interface{}) bool {\n\t\t\t\tcn++\n\t\t\t\tif cn >= c.ipLimit {\n\t\t\t\t\tlimit = true\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t})\n\t\t\tif limit {\n\t\t\t\t// over ip limit\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif c.realtime {\n\t\t\t\tif isTcp {\n\t\t\t\t\tips.Store(ip, 2)\n\t\t\t\t} else {\n\t\t\t\t\tips.Store(ip, 1)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tips.Store(ip, time.Now())\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n// DelConnCount Delete tcp connection count, no tcp do not use\nfunc (c *ConnLimiter) DelConnCount(user string, ip string) {\n\tif !c.realtime {\n\t\treturn\n\t}\n\tif c.connLimit != 0 {\n\t\tif v, ok := c.count.Load(user); ok {\n\t\t\tif v.(int) == 1 {\n\t\t\t\tc.count.Delete(user)\n\t\t\t} else {\n\t\t\t\tc.count.Store(user, v.(int)-1)\n\t\t\t}\n\t\t}\n\t}\n\tif c.ipLimit == 0 {\n\t\treturn\n\t}\n\tif i, ok := c.ip.Load(user); ok {\n\t\tis := i.(*sync.Map)\n\t\tif i, ok := is.Load(ip); ok {\n\t\t\tif i.(int) == 2 {\n\t\t\t\tis.Delete(ip)\n\t\t\t} else {\n\t\t\t\tis.Store(user, i.(int)-2)\n\t\t\t}\n\t\t\tnotDel := false\n\t\t\tc.ip.Range(func(_, _ any) bool {\n\t\t\t\tnotDel = true\n\t\t\t\treturn false\n\t\t\t})\n\t\t\tif !notDel {\n\t\t\t\tc.ip.Delete(user)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// ClearOnlineIP Clear udp,icmp and other packet protocol online ip\nfunc (c *ConnLimiter) ClearOnlineIP() {\n\tc.ip.Range(func(u, v any) bool {\n\t\tuserIp := v.(*sync.Map)\n\t\tnotDel := false\n\t\tuserIp.Range(func(ip, v any) bool {\n\t\t\tnotDel = true\n\t\t\tif _, ok := v.(int); ok {\n\t\t\t\tif v.(int) == 1 {\n\t\t\t\t\t// clear packet ip for realtime\n\t\t\t\t\tuserIp.Delete(ip)\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t} else {\n\t\t\t\t// clear ip for not realtime\n\t\t\t\tif v.(time.Time).Before(time.Now().Add(time.Minute)) {\n\t\t\t\t\t// 1 minute no active\n\t\t\t\t\tuserIp.Delete(ip)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t\tif !notDel {\n\t\t\tc.ip.Delete(u)\n\t\t}\n\t\treturn true\n\t})\n}\n"
  },
  {
    "path": "limiter/conn_test.go",
    "content": "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)\n}\n\nfunc TestConnLimiter_AddConnCount(t *testing.T) {\n\tt.Log(c.AddConnCount(\"1\", \"1\", true))\n\tt.Log(c.AddConnCount(\"1\", \"2\", true))\n}\n\nfunc TestConnLimiter_DelConnCount(t *testing.T) {\n\tt.Log(c.AddConnCount(\"1\", \"1\", true))\n\tt.Log(c.AddConnCount(\"1\", \"2\", true))\n\tc.DelConnCount(\"1\", \"1\")\n\tt.Log(c.AddConnCount(\"1\", \"2\", true))\n}\n\nfunc TestConnLimiter_ClearOnlineIP(t *testing.T) {\n\tt.Log(c.AddConnCount(\"1\", \"1\", false))\n\tt.Log(c.AddConnCount(\"1\", \"2\", false))\n\tc.ClearOnlineIP()\n\tt.Log(c.AddConnCount(\"1\", \"2\", true))\n\tc.DelConnCount(\"1\", \"2\")\n\tt.Log(c.AddConnCount(\"1\", \"1\", false))\n\t// not realtime\n\tc.realtime = false\n\tt.Log(c.AddConnCount(\"3\", \"2\", true))\n\tc.ClearOnlineIP()\n\tt.Log(c.ip.Load(\"3\"))\n\ttime.Sleep(time.Minute)\n\tc.ClearOnlineIP()\n\tt.Log(c.ip.Load(\"3\"))\n}\n\nfunc BenchmarkConnLimiter(b *testing.B) {\n\twg := sync.WaitGroup{}\n\tfor i := 0; i < b.N; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tc.AddConnCount(\"1\", \"2\", true)\n\t\t\tc.DelConnCount(\"1\", \"2\")\n\t\t\twg.Done()\n\t\t}()\n\t}\n\twg.Wait()\n\n}\n"
  },
  {
    "path": "limiter/dynamic.go",
    "content": "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\nfunc (l *Limiter) AddDynamicSpeedLimit(tag string, userInfo *panel.UserInfo, limitNum int, expire int64) error {\n\tuserLimit := &UserLimitInfo{\n\t\tDynamicSpeedLimit: limitNum,\n\t\tExpireTime:        time.Now().Add(time.Duration(expire) * time.Second).Unix(),\n\t}\n\tl.UserLimitInfo.Store(format.UserTag(tag, userInfo.Uuid), userLimit)\n\treturn nil\n}\n\n// determineSpeedLimit returns the minimum non-zero rate\nfunc determineSpeedLimit(limit1, limit2 int) (limit int) {\n\tif limit1 == 0 || limit2 == 0 {\n\t\tif limit1 > limit2 {\n\t\t\treturn limit1\n\t\t} else if limit1 < limit2 {\n\t\t\treturn limit2\n\t\t} else {\n\t\t\treturn 0\n\t\t}\n\t} else {\n\t\tif limit1 > limit2 {\n\t\t\treturn limit2\n\t\t} else if limit1 < limit2 {\n\t\t\treturn limit1\n\t\t} else {\n\t\t\treturn limit1\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "limiter/limiter.go",
    "content": "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/Yuzuki616/V2bX/common/format\"\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\t\"github.com/juju/ratelimit\"\n\tlog \"github.com/sirupsen/logrus\"\n\t\"github.com/xtls/xray-core/common/task\"\n)\n\nvar limitLock sync.RWMutex\nvar limiter map[string]*Limiter\n\nfunc Init() {\n\tlimiter = map[string]*Limiter{}\n\tc := task.Periodic{\n\t\tInterval: time.Minute * 2,\n\t\tExecute:  ClearOnlineIP,\n\t}\n\tgo func() {\n\t\tlog.WithField(\"Type\", \"Limiter\").\n\t\t\tDebug(\"ClearOnlineIP started\")\n\t\ttime.Sleep(time.Minute * 2)\n\t\t_ = c.Start()\n\t}()\n}\n\ntype Limiter struct {\n\tDomainRules   []*regexp.Regexp\n\tProtocolRules []string\n\tSpeedLimit    int\n\tUserLimitInfo *sync.Map    // Key: Uid value: UserLimitInfo\n\tConnLimiter   *ConnLimiter // Key: Uid value: ConnLimiter\n\tSpeedLimiter  *sync.Map    // key: Uid, value: *ratelimit.Bucket\n}\n\ntype UserLimitInfo struct {\n\tUID               int\n\tSpeedLimit        int\n\tDynamicSpeedLimit int\n\tExpireTime        int64\n}\n\nfunc AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo) *Limiter {\n\tinfo := &Limiter{\n\t\tSpeedLimit:    l.SpeedLimit,\n\t\tUserLimitInfo: new(sync.Map),\n\t\tConnLimiter:   NewConnLimiter(l.ConnLimit, l.IPLimit, l.EnableRealtime),\n\t\tSpeedLimiter:  new(sync.Map),\n\t}\n\tfor i := range users {\n\t\tif users[i].SpeedLimit != 0 {\n\t\t\tuserLimit := &UserLimitInfo{\n\t\t\t\tUID:        users[i].Id,\n\t\t\t\tSpeedLimit: users[i].SpeedLimit,\n\t\t\t}\n\t\t\tinfo.UserLimitInfo.Store(format.UserTag(tag, users[i].Uuid), userLimit)\n\t\t}\n\t}\n\tlimitLock.Lock()\n\tlimiter[tag] = info\n\tlimitLock.Unlock()\n\treturn info\n}\n\nfunc GetLimiter(tag string) (info *Limiter, err error) {\n\tlimitLock.RLock()\n\tinfo, ok := limiter[tag]\n\tlimitLock.RUnlock()\n\tif !ok {\n\t\treturn nil, errors.New(\"not found\")\n\t}\n\treturn\n}\n\nfunc DeleteLimiter(tag string) {\n\tlimitLock.Lock()\n\tdelete(limiter, tag)\n\tlimitLock.Unlock()\n}\n\nfunc (l *Limiter) UpdateUser(tag string, added []panel.UserInfo, deleted []panel.UserInfo) {\n\tfor i := range deleted {\n\t\tl.UserLimitInfo.Delete(format.UserTag(tag, deleted[i].Uuid))\n\t}\n\tfor i := range added {\n\t\tif added[i].SpeedLimit != 0 {\n\t\t\tuserLimit := &UserLimitInfo{\n\t\t\t\tUID:        added[i].Id,\n\t\t\t\tSpeedLimit: added[i].SpeedLimit,\n\t\t\t\tExpireTime: 0,\n\t\t\t}\n\t\t\tl.UserLimitInfo.Store(format.UserTag(tag, added[i].Uuid), userLimit)\n\t\t}\n\t}\n}\n\nfunc (l *Limiter) UpdateDynamicSpeedLimit(tag, uuid string, limit int, expire time.Time) error {\n\tif v, ok := l.UserLimitInfo.Load(format.UserTag(tag, uuid)); ok {\n\t\tinfo := v.(*UserLimitInfo)\n\t\tinfo.DynamicSpeedLimit = limit\n\t\tinfo.ExpireTime = expire.Unix()\n\t} else {\n\t\treturn errors.New(\"not found\")\n\t}\n\treturn nil\n}\n\nfunc (l *Limiter) CheckLimit(email string, ip string, isTcp bool) (Bucket *ratelimit.Bucket, Reject bool) {\n\t// ip and conn limiter\n\tif l.ConnLimiter.AddConnCount(email, ip, isTcp) {\n\t\treturn nil, true\n\t}\n\t// check and gen speed limit Bucket\n\tnodeLimit := l.SpeedLimit\n\tuserLimit := 0\n\tif v, ok := l.UserLimitInfo.Load(email); ok {\n\t\tu := v.(*UserLimitInfo)\n\t\tif u.ExpireTime < time.Now().Unix() && u.ExpireTime != 0 {\n\t\t\tif u.SpeedLimit != 0 {\n\t\t\t\tuserLimit = u.SpeedLimit\n\t\t\t\tu.DynamicSpeedLimit = 0\n\t\t\t\tu.ExpireTime = 0\n\t\t\t} else {\n\t\t\t\tl.UserLimitInfo.Delete(email)\n\t\t\t}\n\t\t} else {\n\t\t\tuserLimit = determineSpeedLimit(u.SpeedLimit, u.DynamicSpeedLimit)\n\t\t}\n\t}\n\tlimit := int64(determineSpeedLimit(nodeLimit, userLimit)) * 1000000 / 8 // If you need the Speed limit\n\tif limit > 0 {\n\t\tBucket = ratelimit.NewBucketWithQuantum(time.Second, limit, limit) // Byte/s\n\t\tif v, ok := l.SpeedLimiter.LoadOrStore(email, Bucket); ok {\n\t\t\treturn v.(*ratelimit.Bucket), false\n\t\t} else {\n\t\t\tl.SpeedLimiter.Store(email, Bucket)\n\t\t\treturn Bucket, false\n\t\t}\n\t} else {\n\t\treturn nil, false\n\t}\n}\n\ntype UserIpList struct {\n\tUid    int      `json:\"Uid\"`\n\tIpList []string `json:\"Ips\"`\n}\n\nfunc determineDeviceLimit(nodeLimit, userLimit int) (limit int) {\n\tif nodeLimit == 0 || userLimit == 0 {\n\t\tif nodeLimit > userLimit {\n\t\t\treturn nodeLimit\n\t\t} else if nodeLimit < userLimit {\n\t\t\treturn userLimit\n\t\t} else {\n\t\t\treturn 0\n\t\t}\n\t} else {\n\t\tif nodeLimit > userLimit {\n\t\t\treturn userLimit\n\t\t} else if nodeLimit < userLimit {\n\t\t\treturn nodeLimit\n\t\t} else {\n\t\t\treturn nodeLimit\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "limiter/rule.go",
    "content": "package limiter\n\nimport (\n\t\"regexp\"\n\n\t\"github.com/Yuzuki616/V2bX/api/panel\"\n)\n\nfunc (l *Limiter) CheckDomainRule(destination string) (reject bool) {\n\t// have rule\n\tfor i := range l.DomainRules {\n\t\tif l.DomainRules[i].MatchString(destination) {\n\t\t\treject = true\n\t\t\tbreak\n\t\t}\n\t}\n\treturn\n}\n\nfunc (l *Limiter) CheckProtocolRule(protocol string) (reject bool) {\n\tfor i := range l.ProtocolRules {\n\t\tif l.ProtocolRules[i] == protocol {\n\t\t\treject = true\n\t\t\tbreak\n\t\t}\n\t}\n\treturn\n}\n\nfunc (l *Limiter) UpdateRule(rule *panel.Rules) error {\n\tl.DomainRules = make([]*regexp.Regexp, len(rule.Regexp))\n\tfor i := range rule.Regexp {\n\t\tl.DomainRules[i] = regexp.MustCompile(rule.Regexp[i])\n\t}\n\tl.ProtocolRules = rule.Protocol\n\treturn nil\n}\n"
  },
  {
    "path": "main.go",
    "content": "package main\n\nimport \"github.com/Yuzuki616/V2bX/cmd\"\n\nfunc main() {\n\tcmd.Run()\n}\n"
  },
  {
    "path": "node/cert.go",
    "content": "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\nfunc (c *Controller) renewCertTask() error {\n\tl, err := lego.New(c.CertConfig)\n\tif err != nil {\n\t\tlog.Print(\"new lego error: \", err)\n\t\treturn nil\n\t}\n\terr = l.RenewCert()\n\tif err != nil {\n\t\tlog.Print(\"renew cert error: \", err)\n\t}\n\treturn nil\n}\n\nfunc (c *Controller) requestCert() error {\n\tswitch c.CertConfig.CertMode {\n\tcase \"reality\", \"none\", \"\":\n\t\treturn nil\n\tcase \"file\":\n\t\tif c.CertConfig.CertFile == \"\" || c.CertConfig.KeyFile == \"\" {\n\t\t\treturn fmt.Errorf(\"cert file path or key file path not exist\")\n\t\t}\n\t\treturn nil\n\tcase \"dns\", \"http\":\n\t\tif c.CertConfig.CertFile == \"\" || c.CertConfig.KeyFile == \"\" {\n\t\t\treturn fmt.Errorf(\"cert file path or key file path not exist\")\n\t\t}\n\t\tif file.IsExist(c.CertConfig.CertFile) && file.IsExist(c.CertConfig.KeyFile) {\n\t\t\treturn nil\n\t\t}\n\t\tl, err := lego.New(c.CertConfig)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"create lego object error: %s\", err)\n\t\t}\n\t\terr = l.CreateCert()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"create cert error: %s\", err)\n\t\t}\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"unsupported certmode: %s\", c.CertConfig.CertMode)\n}\n"
  },
  {
    "path": "node/controller.go",
    "content": "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/panel\"\n\t\"github.com/Yuzuki616/V2bX/common/task\"\n\t\"github.com/Yuzuki616/V2bX/conf\"\n\tvCore \"github.com/Yuzuki616/V2bX/core\"\n\t\"github.com/Yuzuki616/V2bX/limiter\"\n\tlog \"github.com/sirupsen/logrus\"\n)\n\ntype Controller struct {\n\tserver                    vCore.Core\n\tapiClient                 *panel.Client\n\ttag                       string\n\tlimiter                   *limiter.Limiter\n\ttraffic                   map[string]int64\n\tuserList                  []panel.UserInfo\n\tinfo                      *panel.NodeInfo\n\tipRecorder                iprecoder.IpRecorder\n\tnodeInfoMonitorPeriodic   *task.Task\n\tuserReportPeriodic        *task.Task\n\trenewCertPeriodic         *task.Task\n\tdynamicSpeedLimitPeriodic *task.Task\n\tonlineIpReportPeriodic    *task.Task\n\t*conf.ControllerConfig\n}\n\n// NewController return a Node controller with default parameters.\nfunc NewController(server vCore.Core, api *panel.Client, config *conf.ControllerConfig) *Controller {\n\tcontroller := &Controller{\n\t\tserver:           server,\n\t\tControllerConfig: config,\n\t\tapiClient:        api,\n\t}\n\treturn controller\n}\n\n// Start implement the Start() function of the service interface\nfunc (c *Controller) Start() error {\n\t// First fetch Node Info\n\tvar err error\n\tnode, err := c.apiClient.GetNodeInfo()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get node info error: %s\", err)\n\t}\n\t// Update user\n\tc.userList, err = c.apiClient.GetUserList()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get user list error: %s\", err)\n\t}\n\tif len(c.userList) == 0 {\n\t\treturn errors.New(\"add users error: not have any user\")\n\t}\n\tc.tag = c.buildNodeTag(node)\n\n\t// add limiter\n\tl := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList)\n\t// add rule limiter\n\tif err = l.UpdateRule(&node.Rules); err != nil {\n\t\treturn fmt.Errorf(\"update rule error: %s\", err)\n\t}\n\tc.limiter = l\n\tif node.Tls || node.Type == \"hysteria\" {\n\t\terr = c.requestCert()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"request cert error: %s\", err)\n\t\t}\n\t}\n\t// Add new tag\n\terr = c.server.AddNode(c.tag, node, c.ControllerConfig)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"add new node error: %s\", err)\n\t}\n\tadded, err := c.server.AddUsers(&vCore.AddUsersParams{\n\t\tTag:      c.tag,\n\t\tConfig:   c.ControllerConfig,\n\t\tUserInfo: c.userList,\n\t\tNodeInfo: node,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"add users error: %s\", err)\n\t}\n\tlog.WithField(\"tag\", c.tag).Infof(\"Added %d new users\", added)\n\tc.info = node\n\tc.startTasks(node)\n\treturn nil\n}\n\n// Close implement the Close() function of the service interface\nfunc (c *Controller) Close() error {\n\tlimiter.DeleteLimiter(c.tag)\n\tif c.nodeInfoMonitorPeriodic != nil {\n\t\tc.nodeInfoMonitorPeriodic.Close()\n\t}\n\tif c.userReportPeriodic != nil {\n\t\tc.userReportPeriodic.Close()\n\t}\n\tif c.renewCertPeriodic != nil {\n\t\tc.renewCertPeriodic.Close()\n\t}\n\tif c.dynamicSpeedLimitPeriodic != nil {\n\t\tc.dynamicSpeedLimitPeriodic.Close()\n\t}\n\tif c.onlineIpReportPeriodic != nil {\n\t\tc.onlineIpReportPeriodic.Close()\n\t}\n\treturn nil\n}\n\nfunc (c *Controller) buildNodeTag(node *panel.NodeInfo) string {\n\treturn fmt.Sprintf(\"%s-%s-%d\", c.apiClient.APIHost, node.Type, node.Id)\n}\n"
  },
  {
    "path": "node/lego/cert.go",
    "content": "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\"github.com/go-acme/lego/v4/challenge/http01\"\n\t\"github.com/go-acme/lego/v4/providers/dns\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc (l *Lego) SetProvider() error {\n\tswitch l.config.CertMode {\n\tcase \"http\":\n\t\terr := l.client.Challenge.SetHTTP01Provider(http01.NewProviderServer(\"\", \"80\"))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"dns\":\n\t\tfor k, v := range l.config.DNSEnv {\n\t\t\tos.Setenv(k, v)\n\t\t}\n\t\tp, err := dns.NewDNSChallengeProviderByName(l.config.Provider)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"create dns challenge provider error: %s\", err)\n\t\t}\n\t\terr = l.client.Challenge.SetDNS01Provider(p)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"set dns provider error: %s\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (l *Lego) CreateCert() (err error) {\n\trequest := certificate.ObtainRequest{\n\t\tDomains: []string{l.config.CertDomain},\n\t}\n\tcertificates, err := l.client.Certificate.Obtain(request)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"obtain certificate error: %s\", err)\n\t}\n\terr = l.writeCert(certificates)\n\treturn nil\n}\n\nfunc (l *Lego) RenewCert() error {\n\tfile, err := os.ReadFile(l.config.CertFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"read cert file error: %s\", err)\n\t}\n\tif e, err := l.CheckCert(file); !e {\n\t\treturn nil\n\t} else if err != nil {\n\t\treturn fmt.Errorf(\"check cert error: %s\", err)\n\t}\n\tres, err := l.client.Certificate.Renew(certificate.Resource{\n\t\tDomain:      l.config.CertDomain,\n\t\tCertificate: file,\n\t}, false, false, \"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = l.writeCert(res)\n\treturn nil\n}\n\nfunc (l *Lego) CheckCert(file []byte) (bool, error) {\n\tcert, err := certcrypto.ParsePEMCertificate(file)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tnotAfter := int(time.Until(cert.NotAfter).Hours() / 24.0)\n\tif notAfter > 30 {\n\t\treturn false, nil\n\t}\n\treturn true, nil\n}\nfunc (l *Lego) parseParams(path string) string {\n\tr := strings.NewReplacer(\"{domain}\", l.config.CertDomain,\n\t\t\"{email}\", l.config.Email)\n\treturn r.Replace(path)\n}\nfunc (l *Lego) writeCert(certificates *certificate.Resource) error {\n\terr := checkPath(l.config.CertFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"check path error: %s\", err)\n\t}\n\terr = os.WriteFile(l.parseParams(l.config.CertFile), certificates.Certificate, 0644)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = checkPath(l.config.KeyFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"check path error: %s\", err)\n\t}\n\terr = os.WriteFile(l.parseParams(l.config.KeyFile), certificates.PrivateKey, 0644)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "node/lego/lego.go",
    "content": "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-acme/lego/v4/certcrypto\"\n\t\"github.com/go-acme/lego/v4/lego\"\n\t\"os\"\n\t\"path\"\n)\n\ntype Lego struct {\n\tclient *lego.Client\n\tconfig *conf.CertConfig\n}\n\nfunc New(config *conf.CertConfig) (*Lego, error) {\n\tuser, err := NewUser(path.Join(path.Dir(config.CertFile),\n\t\t\"user\",\n\t\tfmt.Sprintf(\"user-%s.json\", config.Email)),\n\t\tconfig.Email)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create user error: %s\", err)\n\t}\n\tc := lego.NewConfig(user)\n\t//c.CADirURL = \"http://192.168.99.100:4000/directory\"\n\tc.Certificate.KeyType = certcrypto.RSA2048\n\tclient, err := lego.NewClient(c)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tl := Lego{\n\t\tclient: client,\n\t\tconfig: config,\n\t}\n\terr = l.SetProvider()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"set provider error: %s\", err)\n\t}\n\treturn &l, nil\n}\n\nfunc checkPath(p string) error {\n\tif !file.IsExist(path.Dir(p)) {\n\t\terr := os.MkdirAll(path.Dir(p), 0755)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"create dir error: %s\", err)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "node/lego/lego_test.go",
    "content": "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 error\n\tl, err = New(&conf.CertConfig{\n\t\tCertMode:   \"dns\",\n\t\tEmail:      \"test@test.com\",\n\t\tCertDomain: \"test.test.com\",\n\t\tProvider:   \"cloudflare\",\n\t\tDNSEnv: map[string]string{\n\t\t\t\"CF_DNS_API_TOKEN\": \"123\",\n\t\t},\n\t\tCertFile: \"./cert/1.pem\",\n\t\tKeyFile:  \"./cert/1.key\",\n\t})\n\tif err != nil {\n\t\tlog.Println(err)\n\t\tos.Exit(1)\n\t}\n}\n\nfunc TestLego_CreateCertByDns(t *testing.T) {\n\terr := l.CreateCert()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestLego_RenewCert(t *testing.T) {\n\tlog.Println(l.RenewCert())\n}\n"
  },
  {
    "path": "node/lego/user.go",
    "content": "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\"\n\t\"github.com/Yuzuki616/V2bX/common/file\"\n\t\"github.com/go-acme/lego/v4/lego\"\n\t\"github.com/go-acme/lego/v4/registration\"\n\t\"github.com/goccy/go-json\"\n\t\"os\"\n)\n\ntype User struct {\n\tEmail        string                 `json:\"Email\"`\n\tRegistration *registration.Resource `json:\"Registration\"`\n\tkey          crypto.PrivateKey\n\tKeyEncoded   string `json:\"Key\"`\n}\n\nfunc (u *User) GetEmail() string {\n\treturn u.Email\n}\nfunc (u *User) GetRegistration() *registration.Resource {\n\treturn u.Registration\n}\nfunc (u *User) GetPrivateKey() crypto.PrivateKey {\n\treturn u.key\n}\n\nfunc NewUser(path string, email string) (*User, error) {\n\tvar user User\n\tif file.IsExist(path) {\n\t\terr := user.Load(path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif user.Email != email {\n\t\t\tuser.Registration = nil\n\t\t\tuser.Email = email\n\t\t\terr := registerUser(&user, path)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t} else {\n\t\tuser.Email = email\n\t\terr := registerUser(&user, path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn &user, nil\n}\n\nfunc registerUser(user *User, path string) error {\n\tprivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"generate key error: %s\", err)\n\t}\n\tuser.key = privateKey\n\tc := lego.NewConfig(user)\n\tclient, err := lego.NewClient(c)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create lego client error: %s\", err)\n\t}\n\treg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})\n\tif err != nil {\n\t\treturn err\n\t}\n\tuser.Registration = reg\n\terr = user.Save(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"save user error: %s\", err)\n\t}\n\treturn nil\n}\n\nfunc EncodePrivate(privKey *ecdsa.PrivateKey) (string, error) {\n\tencoded, err := x509.MarshalECPrivateKey(privKey)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tpemEncoded := pem.EncodeToMemory(&pem.Block{Type: \"EC PRIVATE KEY\", Bytes: encoded})\n\treturn string(pemEncoded), nil\n}\nfunc (u *User) Save(path string) error {\n\terr := checkPath(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"check path error: %s\", err)\n\t}\n\tu.KeyEncoded, _ = EncodePrivate(u.key.(*ecdsa.PrivateKey))\n\tf, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = json.NewEncoder(f).Encode(u)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"marshal json error: %s\", err)\n\t}\n\tu.KeyEncoded = \"\"\n\treturn nil\n}\n\nfunc (u *User) DecodePrivate(pemEncodedPriv string) (*ecdsa.PrivateKey, error) {\n\tblockPriv, _ := pem.Decode([]byte(pemEncodedPriv))\n\tx509EncodedPriv := blockPriv.Bytes\n\tprivateKey, err := x509.ParseECPrivateKey(x509EncodedPriv)\n\treturn privateKey, err\n}\nfunc (u *User) Load(path string) error {\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open file error: %s\", err)\n\t}\n\terr = json.NewDecoder(f).Decode(u)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unmarshal json error: %s\", err)\n\t}\n\tu.key, err = u.DecodePrivate(u.KeyEncoded)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"decode private key error: %s\", err)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "node/node.go",
    "content": "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.com/Yuzuki616/V2bX/core\"\n)\n\ntype Node struct {\n\tcontrollers []*Controller\n}\n\nfunc New() *Node {\n\treturn &Node{}\n}\n\nfunc (n *Node) Start(nodes []*conf.NodeConfig, core vCore.Core) error {\n\tn.controllers = make([]*Controller, len(nodes))\n\tfor i, c := range nodes {\n\t\tp, err := panel.New(c.ApiConfig)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// Register controller service\n\t\tn.controllers[i] = NewController(core, p, c.ControllerConfig)\n\t\terr = n.controllers[i].Start()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"start node controller [%s-%s-%d] error: %s\",\n\t\t\t\tc.ApiConfig.NodeType,\n\t\t\t\tc.ApiConfig.APIHost,\n\t\t\t\tc.ApiConfig.NodeID,\n\t\t\t\terr)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (n *Node) Close() {\n\tfor _, c := range n.controllers {\n\t\terr := c.Close()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\tn.controllers = nil\n}\n"
  },
  {
    "path": "node/task.go",
    "content": "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 \"github.com/Yuzuki616/V2bX/core\"\n\t\"github.com/Yuzuki616/V2bX/limiter\"\n\tlog \"github.com/sirupsen/logrus\"\n)\n\nfunc (c *Controller) startTasks(node *panel.NodeInfo) {\n\t// fetch node info task\n\tc.nodeInfoMonitorPeriodic = &task.Task{\n\t\tInterval: node.PullInterval,\n\t\tExecute:  c.nodeInfoMonitor,\n\t}\n\t// fetch user list task\n\tc.userReportPeriodic = &task.Task{\n\t\tInterval: node.PushInterval,\n\t\tExecute:  c.reportUserTrafficTask,\n\t}\n\tlog.WithField(\"tag\", c.tag).Info(\"Start monitor node status\")\n\t// delay to start nodeInfoMonitor\n\t_ = c.nodeInfoMonitorPeriodic.Start(false)\n\tlog.WithField(\"tag\", c.tag).Info(\"Start report node status\")\n\t_ = c.userReportPeriodic.Start(false)\n\tif node.Tls {\n\t\tswitch c.CertConfig.CertMode {\n\t\tcase \"reality\", \"none\", \"\":\n\t\tdefault:\n\t\t\tc.renewCertPeriodic = &task.Task{\n\t\t\t\tInterval: time.Hour * 24,\n\t\t\t\tExecute:  c.renewCertTask,\n\t\t\t}\n\t\t\tlog.WithField(\"tag\", c.tag).Info(\"Start renew cert\")\n\t\t\t// delay to start renewCert\n\t\t\t_ = c.renewCertPeriodic.Start(true)\n\t\t}\n\t}\n\tif c.LimitConfig.EnableDynamicSpeedLimit {\n\t\tc.traffic = make(map[string]int64)\n\t\tc.dynamicSpeedLimitPeriodic = &task.Task{\n\t\t\tInterval: time.Duration(c.LimitConfig.DynamicSpeedLimitConfig.Periodic) * time.Second,\n\t\t\tExecute:  c.SpeedChecker,\n\t\t}\n\t}\n}\n\nfunc (c *Controller) nodeInfoMonitor() (err error) {\n\t// get node info\n\tnewNodeInfo, err := c.apiClient.GetNodeInfo()\n\tif err != nil {\n\t\tlog.WithFields(log.Fields{\n\t\t\t\"tag\": c.tag,\n\t\t\t\"err\": err,\n\t\t}).Error(\"Get node info failed\")\n\t\treturn nil\n\t}\n\t// get user info\n\tnewUserInfo, err := c.apiClient.GetUserList()\n\tif err != nil {\n\t\tlog.WithFields(log.Fields{\n\t\t\t\"tag\": c.tag,\n\t\t\t\"err\": err,\n\t\t}).Error(\"Get user list failed\")\n\t\treturn nil\n\t}\n\tif newNodeInfo != nil {\n\t\t// nodeInfo changed\n\t\tif newUserInfo != nil {\n\t\t\tc.userList = newUserInfo\n\t\t}\n\t\tc.traffic = make(map[string]int64)\n\t\t// Remove old tag\n\t\tlog.WithField(\"tag\", c.tag).Info(\"Node changed, reload\")\n\t\terr = c.server.DelNode(c.tag)\n\t\tif err != nil {\n\t\t\tlog.WithFields(log.Fields{\n\t\t\t\t\"tag\": c.tag,\n\t\t\t\t\"err\": err,\n\t\t\t}).Error(\"Delete node failed\")\n\t\t\treturn nil\n\t\t}\n\t\t// Remove Old limiter\n\t\tlimiter.DeleteLimiter(c.tag)\n\t\t// Add new Limiter\n\t\tc.tag = c.buildNodeTag(newNodeInfo)\n\t\tl := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList)\n\t\t// check cert\n\t\tif newNodeInfo.Tls || newNodeInfo.Type == \"hysteria\" {\n\t\t\terr = c.requestCert()\n\t\t\tif err != nil {\n\t\t\t\tlog.WithFields(log.Fields{\n\t\t\t\t\t\"tag\": c.tag,\n\t\t\t\t\t\"err\": err,\n\t\t\t\t}).Error(\"Request cert failed\")\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\t// add new node\n\t\terr = c.server.AddNode(c.tag, newNodeInfo, c.ControllerConfig)\n\t\tif err != nil {\n\t\t\tlog.WithFields(log.Fields{\n\t\t\t\t\"tag\": c.tag,\n\t\t\t\t\"err\": err,\n\t\t\t}).Error(\"Add node failed\")\n\t\t\treturn nil\n\t\t}\n\t\t_, err = c.server.AddUsers(&vCore.AddUsersParams{\n\t\t\tTag:      c.tag,\n\t\t\tConfig:   c.ControllerConfig,\n\t\t\tUserInfo: c.userList,\n\t\t\tNodeInfo: newNodeInfo,\n\t\t})\n\t\tif err != nil {\n\t\t\tlog.WithFields(log.Fields{\n\t\t\t\t\"tag\": c.tag,\n\t\t\t\t\"err\": err,\n\t\t\t}).Error(\"Add users failed\")\n\t\t\treturn nil\n\t\t}\n\t\terr = l.UpdateRule(&newNodeInfo.Rules)\n\t\tif err != nil {\n\t\t\tlog.WithFields(log.Fields{\n\t\t\t\t\"tag\": c.tag,\n\t\t\t\t\"err\": err,\n\t\t\t}).Error(\"Update Rule failed\")\n\t\t\treturn nil\n\t\t}\n\t\tc.limiter = l\n\t\t// Check interval\n\t\tif c.nodeInfoMonitorPeriodic.Interval != newNodeInfo.PullInterval &&\n\t\t\tnewNodeInfo.PullInterval != 0 {\n\t\t\tc.nodeInfoMonitorPeriodic.Interval = newNodeInfo.PullInterval\n\t\t\tc.nodeInfoMonitorPeriodic.Close()\n\t\t\t_ = c.nodeInfoMonitorPeriodic.Start(false)\n\t\t}\n\t\tif c.userReportPeriodic.Interval != newNodeInfo.PushInterval &&\n\t\t\tnewNodeInfo.PushInterval != 0 {\n\t\t\tc.userReportPeriodic.Interval = newNodeInfo.PullInterval\n\t\t\tc.userReportPeriodic.Close()\n\t\t\t_ = c.userReportPeriodic.Start(false)\n\t\t}\n\t\tlog.WithField(\"tag\", c.tag).Infof(\"Added %d new users\", len(c.userList))\n\t\tc.info = newNodeInfo\n\t\t// exit\n\t\treturn nil\n\t}\n\n\t// node no changed, check users\n\tif len(newUserInfo) == 0 {\n\t\treturn nil\n\t}\n\tdeleted, added := compareUserList(c.userList, newUserInfo)\n\tif len(deleted) > 0 {\n\t\t// have deleted users\n\t\terr = c.server.DelUsers(deleted, c.tag)\n\t\tif err != nil {\n\t\t\tlog.WithFields(log.Fields{\n\t\t\t\t\"tag\": c.tag,\n\t\t\t\t\"err\": err,\n\t\t\t}).Error(\"Delete users failed\")\n\t\t\treturn nil\n\t\t}\n\t}\n\tif len(added) > 0 {\n\t\t// have added users\n\t\t_, err = c.server.AddUsers(&vCore.AddUsersParams{\n\t\t\tTag:      c.tag,\n\t\t\tConfig:   c.ControllerConfig,\n\t\t\tNodeInfo: c.info,\n\t\t\tUserInfo: added,\n\t\t})\n\t\tif err != nil {\n\t\t\tlog.WithFields(log.Fields{\n\t\t\t\t\"tag\": c.tag,\n\t\t\t\t\"err\": err,\n\t\t\t}).Error(\"Add users failed\")\n\t\t\treturn nil\n\t\t}\n\t}\n\tif len(added) > 0 || len(deleted) > 0 {\n\t\t// update Limiter\n\t\tc.limiter.UpdateUser(c.tag, added, deleted)\n\t\tif err != nil {\n\t\t\tlog.WithFields(log.Fields{\n\t\t\t\t\"tag\": c.tag,\n\t\t\t\t\"err\": err,\n\t\t\t}).Error(\"limiter users failed\")\n\t\t\treturn nil\n\t\t}\n\t\t// clear traffic record\n\t\tif c.LimitConfig.EnableDynamicSpeedLimit {\n\t\t\tfor i := range deleted {\n\t\t\t\tdelete(c.traffic, deleted[i].Uuid)\n\t\t\t}\n\t\t}\n\t}\n\tc.userList = newUserInfo\n\tif len(added)+len(deleted) != 0 {\n\t\tlog.WithField(\"tag\", c.tag).\n\t\t\tInfof(\"%d user deleted, %d user added\", len(deleted), len(added))\n\t}\n\treturn nil\n}\n\nfunc (c *Controller) SpeedChecker() error {\n\tfor u, t := range c.traffic {\n\t\tif t >= c.LimitConfig.DynamicSpeedLimitConfig.Traffic {\n\t\t\terr := c.limiter.UpdateDynamicSpeedLimit(c.tag, u,\n\t\t\t\tc.LimitConfig.DynamicSpeedLimitConfig.SpeedLimit,\n\t\t\t\ttime.Now().Add(time.Duration(c.LimitConfig.DynamicSpeedLimitConfig.ExpireTime)*time.Minute))\n\t\t\tlog.WithField(\"err\", err).Error(\"Update dynamic speed limit failed\")\n\t\t\tdelete(c.traffic, u)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "node/user.go",
    "content": "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)\n\nfunc (c *Controller) reportUserTrafficTask() (err error) {\n\t// Get User traffic\n\tuserTraffic := make([]panel.UserTraffic, 0)\n\tfor i := range c.userList {\n\t\tup, down := c.server.GetUserTraffic(c.tag, c.userList[i].Uuid, true)\n\t\tif up > 0 || down > 0 {\n\t\t\tif c.LimitConfig.EnableDynamicSpeedLimit {\n\t\t\t\tif _, ok := c.traffic[c.userList[i].Uuid]; ok {\n\t\t\t\t\tc.traffic[c.userList[i].Uuid] += up + down\n\t\t\t\t} else {\n\t\t\t\t\tc.traffic[c.userList[i].Uuid] = up + down\n\t\t\t\t}\n\t\t\t}\n\t\t\tuserTraffic = append(userTraffic, panel.UserTraffic{\n\t\t\t\tUID:      (c.userList)[i].Id,\n\t\t\t\tUpload:   up,\n\t\t\t\tDownload: down})\n\t\t}\n\t}\n\tif len(userTraffic) > 0 {\n\t\terr = c.apiClient.ReportUserTraffic(userTraffic)\n\t\tif err != nil {\n\t\t\tlog.WithFields(log.Fields{\n\t\t\t\t\"tag\": c.tag,\n\t\t\t\t\"err\": err,\n\t\t\t}).Info(\"Report user traffic failed\")\n\t\t} else {\n\t\t\tlog.WithField(\"tag\", c.tag).Infof(\"Report %d online users\", len(userTraffic))\n\t\t}\n\t}\n\tuserTraffic = nil\n\truntime.GC()\n\treturn nil\n}\n\nfunc compareUserList(old, new []panel.UserInfo) (deleted, added []panel.UserInfo) {\n\ttmp := map[string]struct{}{}\n\ttmp2 := map[string]struct{}{}\n\tfor i := range old {\n\t\ttmp[old[i].Uuid+strconv.Itoa(old[i].SpeedLimit)] = struct{}{}\n\t}\n\tl := len(tmp)\n\tfor i := range new {\n\t\te := new[i].Uuid + strconv.Itoa(new[i].SpeedLimit)\n\t\ttmp[e] = struct{}{}\n\t\ttmp2[e] = struct{}{}\n\t\tif l != len(tmp) {\n\t\t\tadded = append(added, new[i])\n\t\t\tl++\n\t\t}\n\t}\n\ttmp = nil\n\tl = len(tmp2)\n\tfor i := range old {\n\t\ttmp2[old[i].Uuid+strconv.Itoa(old[i].SpeedLimit)] = struct{}{}\n\t\tif l != len(tmp2) {\n\t\t\tdeleted = append(deleted, old[i])\n\t\t\tl++\n\t\t}\n\t}\n\treturn deleted, added\n}\n"
  },
  {
    "path": "test_data/1.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAyryUpSI01T4jgPywpt4CIaf+MNgPn9DxHtJmuo1VB7Ysk13Z\nuOBVfS9HJoikbrcVeqENkR8Bq1vdgMLbHmlxrSTwe/pynmRvWx+L1hKscxEmR6uV\njgqWsPkoUAKVJlHBfjw6oFEQzmgwDOfyS90TVr4gwDo/GCLX1iX5jC+KrteuAMOp\nV6Yiu1FAhd0TeUDPMKs4wbyE8qx3+Mn6FqGorIwyefCX9m9QCz1N/5Ut6I5uT5Vr\n28Ox1Q++mZ0s5ZuFpMlzKvrfav7fxt31rgSUro7/NTNuIydHvwMcZYeVm+DT+Vfp\nwRZW9uoB4jiwLDxnfMOMLd72yGId+9EJ6HpOIQIDAQABAoIBAHhV/xUVfK6mN4S0\neFZTqIg5otNzK7L83mIhGQDaKwJsy4CdUEJARf4MNftVV+Svn3wuZFMjSGZiHNP0\n1QL0K5lON8AfJDGIA+DelK34X4vdPg+EdTzeZBufiKIVJlqcZHF9Zn8KHyOlDABd\nHKCTFIuERwRSjmjRJbPizoC7J2Ina15pi+35T3x3oye4V5kQlJH3WxlMOhp0Z+iO\nqGlqJITWeOqxXaLfPIoOYcJ2LBPzvgv4IRVbmhi4mxphfPTEAlGnvITauDa9fx7S\nREBAFyt5NyU9M7dusmlLiTIHryOensBzCQOwbMseeUS0Z1rIfbusqwBM3X5oPcx8\njOP1QxECgYEA+qCU4Q84hDBhC2SLfH4D1QyOixeho5re6E0NchSposIhrxLdRDDk\n04GGNTKUflgfoskaI9loe84MkTqqrjxguRWVXf5qPMAtfpXr2GYiPq+VhiEeo7ZG\nf1rHYqK7Ity9jzy7z/CpTLekveUT93I7/yF5iEOUYQgnIZhDg+Hr0ysCgYEAzxUu\nDmL7GceRauoD9w3tT2nsa23hXc+jo1QWV5I7veZyosXnz8gxpax4SgwZ/iCH8pDJ\nRagWTHQHO3X6DdkovzPEWYiLtLCE2APlfClzkZif5tv7kO4B3BuyuDlNleuCH894\n5WlieyCHcPtXqG/60tXxVrlNrevI+ccsYRb+reMCgYBgu8AaybQnmUCrlAgeacjy\n3yDZYKqbqfflM3BAGueKkWFM4HwUiMaZOAHj4Hzd8wdq3jG/qncgadwB5eHg1B8E\n8Oaw27SHdClbFWRtJqaLCVwt4/SefYjiONiCIosWHprvgSKAVMQTf0IPpS46sJWl\nmHb++A56ERqBZfKRIY7S9wKBgCKf+flx12ZyFgB4bH1MmNdkcKFt1/bllwjiMHIo\nA1E3TQema6I0aQi4k8xdxaLWMaT/TIgXGNNjuynYCh1yp/uAXl5SFHn74dp0nFRs\nYeSATow9UAzlnu38u59OBYkBvdovyJkjS9ImmD7t57RENP43w4iqpzBjclFBWkxJ\nmf/dAoGBAOZa8+FmFyx2VmgwR4qPrNKg6EuhO6OgDJ8NvRnx4Mu5/0L1JoYoM0Yc\n2NMKh14fqmDB/CvCbMGxCj86WF8534t+eBqhY6/AUkSolh8139KNmYHNB0ynqDYn\nQDm/HJ4cux2rkLUCKlbGBnS9M+PSJA/MJeh0JTIrSOu4bH7aanVl\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "test_data/1.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIID2zCCAsOgAwIBAgIRAJP0pRlp9k2eiBLK2a2B7LYwDQYJKoZIhvcNAQELBQAw\nXjELMAkGA1UEBhMCQ04xDjAMBgNVBAoTBU15U1NMMSswKQYDVQQLEyJNeVNTTCBU\nZXN0IFJTQSAtIEZvciB0ZXN0IHVzZSBvbmx5MRIwEAYDVQQDEwlNeVNTTC5jb20w\nHhcNMjMwNjA4MTQxOTQzWhcNMjgwNjA2MTQxOTQzWjAiMQswCQYDVQQGEwJDTjET\nMBEGA1UEAxMKMTE0NTE0LmdheTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBAMq8lKUiNNU+I4D8sKbeAiGn/jDYD5/Q8R7SZrqNVQe2LJNd2bjgVX0vRyaI\npG63FXqhDZEfAatb3YDC2x5pca0k8Hv6cp5kb1sfi9YSrHMRJkerlY4KlrD5KFAC\nlSZRwX48OqBREM5oMAzn8kvdE1a+IMA6Pxgi19Yl+Ywviq7XrgDDqVemIrtRQIXd\nE3lAzzCrOMG8hPKsd/jJ+hahqKyMMnnwl/ZvUAs9Tf+VLeiObk+Va9vDsdUPvpmd\nLOWbhaTJcyr632r+38bd9a4ElK6O/zUzbiMnR78DHGWHlZvg0/lX6cEWVvbqAeI4\nsCw8Z3zDjC3e9shiHfvRCeh6TiECAwEAAaOBzzCBzDAOBgNVHQ8BAf8EBAMCBaAw\nHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFCiBJgXR\nNBo/wXMPu5PPFRw/A79/MGMGCCsGAQUFBwEBBFcwVTAhBggrBgEFBQcwAYYVaHR0\ncDovL29jc3AubXlzc2wuY29tMDAGCCsGAQUFBzAChiRodHRwOi8vY2EubXlzc2wu\nY29tL215c3NsdGVzdHJzYS5jcnQwFQYDVR0RBA4wDIIKMTE0NTE0LmdheTANBgkq\nhkiG9w0BAQsFAAOCAQEAdlvVKnB1OHojoHfgPKUVTk5+OXk9X/q++wkkrGa1rQs0\nbPikKdc1TQoW/ylpX9wN3rwLLYGf/Hs9SqHr4RkAhAb6v+K1O5HUMqJARONdB7j0\n/1BuKd0wx5pIqJhs6qRf+grsOGj9EdTfKXqElCljAES0t+2ZbZ2666XftwSjybtF\nyKg+9iS9PX5VA1SIsa7XSVTlJ8oXy91KuCm07UxnSEKovpzV4TIlxPgKOfWBUYh9\nJfOhwh1rpUy6tqNjFyJdGHxBJsf7HLcO9VJe/RD55c54ovZaTT24Cy/II5DKiQ26\nTUeMj1BMedu5Ou0YCH7W9QhH40fvwi/hSQrjysQAoA==\n-----END CERTIFICATE-----\n"
  }
]