Full Code of p4gefau1t/trojan-go for AI

master 2dc60f52e79f cached
205 files
513.3 KB
178.2k tokens
1033 symbols
1 requests
Download .txt
Showing preview only (558K chars total). Download the full file or copy to clipboard to get everything.
Repository: p4gefau1t/trojan-go
Branch: master
Commit: 2dc60f52e79f
Files: 205
Total size: 513.3 KB

Directory structure:
gitextract_d00n_ui_/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   └── bug-report.md
│   ├── linters/
│   │   └── .golangci.yml
│   └── workflows/
│       ├── docker-build.yml
│       ├── docker-nightly-build.yml
│       ├── gh-pages.yml
│       ├── linter.yml
│       ├── nightly-build.yml
│       ├── release-build.yml
│       └── test.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── api/
│   ├── api.go
│   ├── control/
│   │   └── control.go
│   └── service/
│       ├── api.pb.go
│       ├── api.proto
│       ├── api_grpc.pb.go
│       ├── client.go
│       ├── client_test.go
│       ├── config.go
│       ├── gen.sh
│       ├── server.go
│       └── server_test.go
├── common/
│   ├── common.go
│   ├── error.go
│   ├── geodata/
│   │   ├── cache.go
│   │   ├── decode.go
│   │   ├── decode_test.go
│   │   ├── interface.go
│   │   └── loader.go
│   ├── io.go
│   ├── io_test.go
│   ├── net.go
│   └── sync.go
├── component/
│   ├── api.go
│   ├── base.go
│   ├── client.go
│   ├── custom.go
│   ├── forward.go
│   ├── mysql.go
│   ├── nat.go
│   ├── other.go
│   └── server.go
├── config/
│   ├── config.go
│   └── config_test.go
├── constant/
│   └── constant.go
├── docs/
│   ├── .gitignore
│   ├── Makefile
│   ├── archetypes/
│   │   └── default.md
│   ├── config.toml
│   └── content/
│       ├── _index.md
│       ├── advance/
│       │   ├── _index.md
│       │   ├── aead.md
│       │   ├── api.md
│       │   ├── customize-protocol-stack.md
│       │   ├── forward.md
│       │   ├── mux.md
│       │   ├── nat.md
│       │   ├── nginx-relay.md
│       │   ├── plugin.md
│       │   ├── router.md
│       │   └── websocket.md
│       ├── basic/
│       │   ├── _index.md
│       │   ├── config.md
│       │   ├── full-config.md
│       │   └── trojan.md
│       └── developer/
│           ├── _index.md
│           ├── api.md
│           ├── build.md
│           ├── mux.md
│           ├── overview.md
│           ├── plugin.md
│           ├── simplesocks.md
│           ├── trojan.md
│           ├── url.md
│           └── websocket.md
├── easy/
│   └── easy.go
├── example/
│   ├── client.json
│   ├── client.yaml
│   ├── server.json
│   ├── server.yaml
│   ├── trojan-go.service
│   └── trojan-go@.service
├── go.mod
├── go.sum
├── log/
│   ├── golog/
│   │   ├── buffer/
│   │   │   ├── buffer.go
│   │   │   └── buffer_test.go
│   │   ├── colorful/
│   │   │   ├── colorful.go
│   │   │   └── colorful_test.go
│   │   └── golog.go
│   ├── log.go
│   └── simplelog/
│       └── simplelog.go
├── main.go
├── option/
│   └── option.go
├── proxy/
│   ├── client/
│   │   ├── client.go
│   │   └── config.go
│   ├── config.go
│   ├── custom/
│   │   ├── config.go
│   │   └── custom.go
│   ├── forward/
│   │   └── forward.go
│   ├── nat/
│   │   ├── nat.go
│   │   └── nat_stub.go
│   ├── option.go
│   ├── proxy.go
│   ├── server/
│   │   ├── config.go
│   │   └── server.go
│   └── stack.go
├── redirector/
│   ├── redirector.go
│   └── redirector_test.go
├── statistic/
│   ├── memory/
│   │   ├── config.go
│   │   ├── memory.go
│   │   └── memory_test.go
│   ├── mysql/
│   │   ├── config.go
│   │   └── mysql.go
│   └── statistics.go
├── test/
│   ├── scenario/
│   │   ├── custom_test.go
│   │   └── proxy_test.go
│   └── util/
│       ├── target.go
│       └── util.go
├── tunnel/
│   ├── adapter/
│   │   ├── config.go
│   │   ├── server.go
│   │   └── tunnel.go
│   ├── dokodemo/
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── dokodemo_test.go
│   │   ├── server.go
│   │   └── tunnel.go
│   ├── freedom/
│   │   ├── client.go
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── freedom_test.go
│   │   └── tunnel.go
│   ├── http/
│   │   ├── http_test.go
│   │   ├── server.go
│   │   └── tunnel.go
│   ├── metadata.go
│   ├── mux/
│   │   ├── client.go
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── mux_test.go
│   │   ├── server.go
│   │   └── tunnel.go
│   ├── router/
│   │   ├── client.go
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── data.go
│   │   ├── router_test.go
│   │   └── tunnel.go
│   ├── shadowsocks/
│   │   ├── client.go
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── server.go
│   │   ├── shadowsocks_test.go
│   │   └── tunnel.go
│   ├── simplesocks/
│   │   ├── client.go
│   │   ├── conn.go
│   │   ├── server.go
│   │   ├── simplesocks_test.go
│   │   └── tunnel.go
│   ├── socks/
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── server.go
│   │   ├── socks_test.go
│   │   └── tunnel.go
│   ├── tls/
│   │   ├── client.go
│   │   ├── config.go
│   │   ├── fingerprint/
│   │   │   └── tls.go
│   │   ├── server.go
│   │   ├── tls_test.go
│   │   └── tunnel.go
│   ├── tproxy/
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── getsockopt.go
│   │   ├── getsockopt_i386.go
│   │   ├── server.go
│   │   ├── tcp.go
│   │   ├── tproxy_stub.go
│   │   ├── tunnel.go
│   │   └── udp.go
│   ├── transport/
│   │   ├── client.go
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── server.go
│   │   ├── transport_test.go
│   │   └── tunnel.go
│   ├── trojan/
│   │   ├── client.go
│   │   ├── config.go
│   │   ├── packet.go
│   │   ├── server.go
│   │   ├── trojan_test.go
│   │   └── tunnel.go
│   ├── tunnel.go
│   └── websocket/
│       ├── client.go
│       ├── config.go
│       ├── conn.go
│       ├── server.go
│       ├── tunnel.go
│       └── websocket_test.go
├── url/
│   ├── option.go
│   ├── option_test.go
│   ├── share_link.go
│   └── share_link_test.go
└── version/
    └── version.go

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

================================================
FILE: .github/ISSUE_TEMPLATE/bug-report.md
================================================
---
name: Bug 报告
about: 提交项目中存在的漏洞和问题
title: "[BUG]"
labels: ''
assignees: ''

---

- [ ] **我确定我已经尝试多次复现此次问题,并且将会提供涉及此问题的系统和网络环境,软件及其版本。**

我们建议您按照下方模板填写 Bug Report,以便我们收集更多的有效信息

## 简单描述这个 Bug

## 如何复现这个 Bug

在此描述复现这个Bug所需要的操作步骤

## 服务器和客户端环境信息

在此描述你的服务器和客户端所处的网络环境,系统架构,以及其他信息

## 服务端和客户端日志

粘贴**故障发生时**,服务端和客户端日志

## 服务端和客户端配置文件

可以复现该问题的客户端和服务端的完整配置(请隐去域名和IP等隐私信息)

## 服务端和客户端版本信息

请执行./trojan-go -version并将输出完整粘贴在此处

## 其他信息

你认为对我们修复bug有帮助的任何信息都可以在这里写出来


================================================
FILE: .github/linters/.golangci.yml
================================================
run:
  timeout: 5m
  skip-files:
    - \.pb\.go$

issues:
  new: true

linters:
  enable:
    - asciicheck
    - bodyclose
    - depguard
    - gci
    - gocritic
    - gofmt
    - gofumpt
    - goimports
    - govet
    - ineffassign
    - misspell
    - typecheck
    - unconvert
    - whitespace
  disable:
    - deadcode
    - errcheck
    - goprintffuncname
    - gosimple
    - nilerr
    - rowserrcheck
    - staticcheck
    - structcheck
    - stylecheck
    - unparam
    - unused
    - varcheck

linters-settings:
  gci:
    local-prefixes: github.com/p4gefau1t/trojan-go
  goimports:
    local-prefixes: github.com/p4gefau1t/trojan-go


================================================
FILE: .github/workflows/docker-build.yml
================================================
on:
  push:
    branches:
      - master
    paths-ignore:
      - "**.md"
      - "docs/**"
    tags:
      - "v*.*.*"
name: docker-build
jobs:
  build:
    if: github.repository == 'p4gefau1t/trojan-go'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout the code
        uses: actions/checkout@v2

      - name: Setup QEMU
        uses: docker/setup-qemu-action@v1

      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v1

      - name: Login to Docker Hub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Prepare
        id: prepare
        run: |
          if [[ $GITHUB_REF == refs/tags/* ]]; then
            echo ::set-output name=version::${GITHUB_REF#refs/tags/}
            echo ::set-output name=ref::${GITHUB_REF#refs/tags/}
          else
            echo ::set-output name=version::snapshot
            echo ::set-output name=ref::${{ github.sha }}
          fi
          echo ::set-output name=docker_platforms::linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
          echo ::set-output name=docker_image::${{ secrets.DOCKER_USERNAME }}/trojan-go

      - name: Build and push docker image
        run: |
          docker buildx build --platform ${{ steps.prepare.outputs.docker_platforms }} \
          --output "type=image,push=true" \
          --tag "${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }}" \
          --tag "${{ steps.prepare.outputs.docker_image }}:latest" \
          --build-arg REF=${{ steps.prepare.outputs.ref }} \
          --file Dockerfile .

  test:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Test docker image
        run: |
          docker run --rm --entrypoint /usr/local/bin/trojan-go ${{ secrets.DOCKER_USERNAME }}/trojan-go -version


================================================
FILE: .github/workflows/docker-nightly-build.yml
================================================
on:
  push:
    branches:
      - master
    paths-ignore:
      - '**.md'
      - 'docs/**'
name: docker-nightly-build
jobs:
  build:
    if: github.repository == 'p4gefau1t/trojan-go'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout the code
        uses: actions/checkout@v2

      - name: Setup QEMU
        uses: docker/setup-qemu-action@v1

      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v1

      - name: Login to Docker Hub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Prepare
        id: prepare
        run: |
          echo ::set-output name=docker_platforms::linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
          echo ::set-output name=docker_image::${{ secrets.DOCKER_USERNAME }}/trojan-go
          echo ::set-output name=ref::${{ github.sha }}

      - name: Build and push docker image
        run: |
          docker buildx build --platform ${{ steps.prepare.outputs.docker_platforms }} \
          --output "type=image,push=true" \
          --tag "${{ steps.prepare.outputs.docker_image }}:nightly" \
          --build-arg REF=${{ steps.prepare.outputs.ref }} \
          --file Dockerfile .

  test:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Test docker image
        run: |
          docker run --rm --entrypoint /usr/local/bin/trojan-go ${{ secrets.DOCKER_USERNAME }}/trojan-go -version


================================================
FILE: .github/workflows/gh-pages.yml
================================================
on:
  push:
    branches:
      - master
    paths:
      - "docs/**"
      - ".github/workflows/gh-pages.yml"
  pull_request:
    types: [opened, synchronize, reopened]
    branches:
      - master
    paths:
      - "docs/**"
      - ".github/workflows/gh-pages.yml"
name: github-pages
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: true  # Fetch Hugo themes
          fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2
        with:
          hugo-version: "0.83.1"
          # extended: true

      - name: Build
        run: |
          cd docs
          make hugo-themes
          hugo

      - name: Deploy
        if: ${{ github.event_name == 'push' }}
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./docs/public


================================================
FILE: .github/workflows/linter.yml
================================================
name: Linter

on:
  push:
    branches:
      - master
    paths:
      - "**/*.go"
      - ".github/workflows/linter.yml"
      - ".github/linters/.golangci.yml"
  pull_request:
    types: [opened, synchronize, reopened]
    branches:
      - master
    paths:
      - "**/*.go"
      - ".github/workflows/linter.yml"
      - ".github/linters/.golangci.yml"

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout codebase
        uses: actions/checkout@v2

      - name: golangci-lint
        uses: golangci/golangci-lint-action@v2
        with:
          version: latest
          args: --config=.github/linters/.golangci.yml
          only-new-issues: true


================================================
FILE: .github/workflows/nightly-build.yml
================================================
on:
  push:
    branches:
      - master
    paths:
      - "**/*.go"
      - "go.mod"
      - "go.sum"
      - "Makefile"
      - ".github/workflows/nightly-build.yml"
  pull_request:
    types: [opened, synchronize, reopened]
    branches:
      - master
    paths:
      - "**/*.go"
      - "go.mod"
      - "go.sum"
      - "Makefile"
      - ".github/workflows/nightly-build.yml"
name: nightly-build
jobs:
  build:
    strategy:
      fail-fast: false
    runs-on: ubuntu-latest
    steps:
      - name: Install Go
        uses: actions/setup-go@v2
        with:
          go-version: ^1.17.1

      - name: Checkout code
        uses: actions/checkout@v2

      - name: Build
        run: |
          make release -j$(nproc)


================================================
FILE: .github/workflows/release-build.yml
================================================
on:
  push:
    tags:
      - "v*.*.*"
name: release-build
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Install Go
        uses: actions/setup-go@v2
        with:
          go-version: ^1.17.1

      - name: Checkout code
        uses: actions/checkout@v2

      - name: Checkout tag
        run: |
          git fetch --depth=1 origin +refs/tags/*:refs/tags/*
          tag_name="${GITHUB_REF##*/}"
          echo Tag $tag_name
          git checkout $tag_name
          echo "TAG_NAME=${tag_name}" >> $GITHUB_ENV

      - name: Build
        run: |
          make release -j$(nproc)

      - name: Release
        uses: svenstaro/upload-release-action@v2
        with:
          repo_token: ${{ secrets.GITHUB_TOKEN }}
          tag: ${{ env.TAG_NAME }}
          file: ./trojan-go-*.zip
          file_glob: true
          prerelease: true


================================================
FILE: .github/workflows/test.yml
================================================
name: Test
on:
  push:
    branches:
      - master
    paths:
      - "**/*.go"
      - "go.mod"
      - "go.sum"
      - ".github/workflows/test.yml"
  pull_request:
    types: [opened, synchronize, reopened]
    branches:
      - master
    paths:
      - "**/*.go"
      - "go.mod"
      - "go.sum"
      - ".github/workflows/test.yml"
jobs:
  test:
    strategy:
      fail-fast: false
      matrix:
        platform: [ubuntu-latest, windows-latest, macos-latest]
    runs-on: ${{ matrix.platform }}
    steps:
      - name: Install Go
        uses: actions/setup-go@v2
        with:
          go-version: ^1.17.1

      - name: Checkout code
        uses: actions/checkout@v2

      - name: Check Go modules
        run: |
          go mod tidy -compat=1.17
          git diff --exit-code go.mod go.sum
          go mod verify

      - name: Test
        run: make test


================================================
FILE: .gitignore
================================================
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

build/
*.DS_Store
*.zip
*.tar.gz
*.crt
*.key
*.dat
trojan-go


================================================
FILE: Dockerfile
================================================
FROM golang:alpine AS builder
WORKDIR /
ARG REF
RUN apk add git make &&\
    git clone https://github.com/p4gefau1t/trojan-go.git
RUN if [[ -z "${REF}" ]]; then \
        echo "No specific commit provided, use the latest one." \
    ;else \
        echo "Use commit ${REF}" &&\
        cd trojan-go &&\
        git checkout ${REF} \
    ;fi
RUN cd trojan-go &&\
    make &&\
    wget https://github.com/v2fly/domain-list-community/raw/release/dlc.dat -O build/geosite.dat &&\
    wget https://github.com/v2fly/geoip/raw/release/geoip.dat -O build/geoip.dat &&\
    wget https://github.com/v2fly/geoip/raw/release/geoip-only-cn-private.dat -O build/geoip-only-cn-private.dat

FROM alpine
WORKDIR /
RUN apk add --no-cache tzdata ca-certificates
COPY --from=builder /trojan-go/build /usr/local/bin/
COPY --from=builder /trojan-go/example/server.json /etc/trojan-go/config.json

ENTRYPOINT ["/usr/local/bin/trojan-go", "-config"]
CMD ["/etc/trojan-go/config.json"]


================================================
FILE: LICENSE
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.


================================================
FILE: Makefile
================================================
NAME := trojan-go
PACKAGE_NAME := github.com/p4gefau1t/trojan-go
VERSION := `git describe --dirty`
COMMIT := `git rev-parse HEAD`

PLATFORM := linux
BUILD_DIR := build
VAR_SETTING := -X $(PACKAGE_NAME)/constant.Version=$(VERSION) -X $(PACKAGE_NAME)/constant.Commit=$(COMMIT)
GOBUILD = env CGO_ENABLED=0 $(GO_DIR)go build -tags "full" -trimpath -ldflags="-s -w -buildid= $(VAR_SETTING)" -o $(BUILD_DIR)

.PHONY: trojan-go release test
normal: clean trojan-go

clean:
	rm -rf $(BUILD_DIR)
	rm -f *.zip
	rm -f *.dat

geoip.dat:
	wget https://github.com/v2fly/geoip/raw/release/geoip.dat

geoip-only-cn-private.dat:
	wget https://github.com/v2fly/geoip/raw/release/geoip-only-cn-private.dat

geosite.dat:
	wget https://github.com/v2fly/domain-list-community/raw/release/dlc.dat -O geosite.dat

test:
	# Disable Bloomfilter when testing
	SHADOWSOCKS_SF_CAPACITY="-1" $(GO_DIR)go test -v ./...

trojan-go:
	mkdir -p $(BUILD_DIR)
	$(GOBUILD)

install: $(BUILD_DIR)/$(NAME) geoip.dat geoip-only-cn-private.dat geosite.dat
	mkdir -p /etc/$(NAME)
	mkdir -p /usr/share/$(NAME)
	cp example/*.json /etc/$(NAME)
	cp $(BUILD_DIR)/$(NAME) /usr/bin/$(NAME)
	cp example/$(NAME).service /usr/lib/systemd/system/
	cp example/$(NAME)@.service /usr/lib/systemd/system/
	systemctl daemon-reload
	cp geosite.dat /usr/share/$(NAME)/geosite.dat
	cp geoip.dat /usr/share/$(NAME)/geoip.dat
	cp geoip-only-cn-private.dat /usr/share/$(NAME)/geoip-only-cn-private.dat
	ln -fs /usr/share/$(NAME)/geoip.dat /usr/bin/
	ln -fs /usr/share/$(NAME)/geoip-only-cn-private.dat /usr/bin/
	ln -fs /usr/share/$(NAME)/geosite.dat /usr/bin/

uninstall:
	rm /usr/lib/systemd/system/$(NAME).service
	rm /usr/lib/systemd/system/$(NAME)@.service
	systemctl daemon-reload
	rm /usr/bin/$(NAME)
	rm -rd /etc/$(NAME)
	rm -rd /usr/share/$(NAME)
	rm /usr/bin/geoip.dat
	rm /usr/bin/geoip-only-cn-private.dat
	rm /usr/bin/geosite.dat

%.zip: % geosite.dat geoip.dat geoip-only-cn-private.dat
	@zip -du $(NAME)-$@ -j $(BUILD_DIR)/$</*
	@zip -du $(NAME)-$@ example/*
	@-zip -du $(NAME)-$@ *.dat
	@echo "<<< ---- $(NAME)-$@"

release: geosite.dat geoip.dat geoip-only-cn-private.dat darwin-amd64.zip darwin-arm64.zip linux-386.zip linux-amd64.zip \
	linux-arm.zip linux-armv5.zip linux-armv6.zip linux-armv7.zip linux-armv8.zip \
	linux-mips-softfloat.zip linux-mips-hardfloat.zip linux-mipsle-softfloat.zip linux-mipsle-hardfloat.zip \
	linux-mips64.zip linux-mips64le.zip freebsd-386.zip freebsd-amd64.zip \
	windows-386.zip windows-amd64.zip windows-arm.zip windows-armv6.zip windows-armv7.zip windows-arm64.zip

darwin-amd64:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=amd64 GOOS=darwin $(GOBUILD)/$@

darwin-arm64:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=arm64 GOOS=darwin $(GOBUILD)/$@

linux-386:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=386 GOOS=linux $(GOBUILD)/$@

linux-amd64:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=amd64 GOOS=linux $(GOBUILD)/$@

linux-arm:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=arm GOOS=linux $(GOBUILD)/$@

linux-armv5:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=arm GOOS=linux GOARM=5 $(GOBUILD)/$@

linux-armv6:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=arm GOOS=linux GOARM=6 $(GOBUILD)/$@

linux-armv7:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=arm GOOS=linux GOARM=7 $(GOBUILD)/$@

linux-armv8:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=arm64 GOOS=linux $(GOBUILD)/$@

linux-mips-softfloat:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=mips GOMIPS=softfloat GOOS=linux $(GOBUILD)/$@

linux-mips-hardfloat:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=mips GOMIPS=hardfloat GOOS=linux $(GOBUILD)/$@

linux-mipsle-softfloat:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=mipsle GOMIPS=softfloat GOOS=linux $(GOBUILD)/$@

linux-mipsle-hardfloat:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=mipsle GOMIPS=hardfloat GOOS=linux $(GOBUILD)/$@

linux-mips64:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=mips64 GOOS=linux $(GOBUILD)/$@

linux-mips64le:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=mips64le GOOS=linux $(GOBUILD)/$@

freebsd-386:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=386 GOOS=freebsd $(GOBUILD)/$@

freebsd-amd64:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=amd64 GOOS=freebsd $(GOBUILD)/$@

windows-386:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=386 GOOS=windows $(GOBUILD)/$@

windows-amd64:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=amd64 GOOS=windows $(GOBUILD)/$@

windows-arm:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=arm GOOS=windows $(GOBUILD)/$@

windows-armv6:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=arm GOOS=windows GOARM=6 $(GOBUILD)/$@

windows-armv7:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=arm GOOS=windows GOARM=7 $(GOBUILD)/$@

windows-arm64:
	mkdir -p $(BUILD_DIR)/$@
	GOARCH=arm64 GOOS=windows $(GOBUILD)/$@


================================================
FILE: README.md
================================================
# Trojan-Go [![Go Report Card](https://goreportcard.com/badge/github.com/p4gefau1t/trojan-go)](https://goreportcard.com/report/github.com/p4gefau1t/trojan-go) [![Downloads](https://img.shields.io/github/downloads/p4gefau1t/trojan-go/total?label=downloads&logo=github&style=flat-square)](https://img.shields.io/github/downloads/p4gefau1t/trojan-go/total?label=downloads&logo=github&style=flat-square)

使用 Go 实现的完整 Trojan 代理,兼容原版 Trojan 协议及配置文件格式。安全、高效、轻巧、易用。

Trojan-Go 支持[多路复用](#多路复用)提升并发性能;使用[路由模块](#路由模块)实现国内外分流;支持 [CDN 流量中转](#Websocket)(基于 WebSocket over TLS);支持使用 AEAD 对 Trojan 流量进行[二次加密](#aead-加密)(基于 Shadowsocks AEAD);支持可插拔的[传输层插件](#传输层插件),允许替换 TLS,使用其他加密隧道传输 Trojan 协议流量。

预编译二进制可执行文件可在 [Release 页面](https://github.com/p4gefau1t/trojan-go/releases)下载。解压后即可直接运行,无其他组件依赖。

如遇到配置和使用问题、发现 bug,或是有更好的想法,欢迎加入 [Telegram 交流反馈群](https://t.me/trojan_go_chat)。

## 简介

**完整介绍和配置教程,参见 [Trojan-Go 文档](https://p4gefau1t.github.io/trojan-go)。**

Trojan-Go 兼容原版 Trojan 的绝大多数功能,包括但不限于:

- TLS 隧道传输
- UDP 代理
- 透明代理 (NAT 模式,iptables 设置参考[这里](https://github.com/shadowsocks/shadowsocks-libev/tree/v3.3.1#transparent-proxy))
- 对抗 GFW 被动检测 / 主动检测的机制
- MySQL 数据持久化方案
- MySQL 用户权限认证
- 用户流量统计和配额限制

同时,Trojan-Go 还扩展实现了更多高效易用的功能特性:

- 便于快速部署的「简易模式」
- Socks5 / HTTP 代理自动适配
- 基于 TProxy 的透明代理(TCP / UDP)
- 全平台支持,无特殊依赖
- 基于多路复用(smux)降低延迟,提升并发性能
- 自定义路由模块,可实现国内外分流 / 广告屏蔽等功能
- Websocket 传输支持,以实现 CDN 流量中转(基于 WebSocket over TLS)和对抗 GFW 中间人攻击
- TLS 指纹伪造,以对抗 GFW 针对 TLS Client Hello 的特征识别
- 基于 gRPC 的 API 支持,以实现用户管理和速度限制等
- 可插拔传输层,可将 TLS 替换为其他协议或明文传输,同时有完整的 Shadowsocks 混淆插件支持
- 支持对用户更友好的 YAML 配置文件格式

## 图形界面客户端

Trojan-Go 服务端兼容所有原 Trojan 客户端,如 Igniter、ShadowRocket 等。以下是支持 Trojan-Go 扩展特性(Websocket / Mux 等)的客户端:

- [Qv2ray](https://github.com/Qv2ray/Qv2ray):跨平台客户端,支持 Windows / macOS / Linux,使用 Trojan-Go 核心,支持所有 Trojan-Go 扩展特性。
- [Igniter-Go](https://github.com/p4gefau1t/trojan-go-android):Android 客户端,Fork 自 Igniter,将 Igniter 核心替换为 Trojan-Go 并做了一定修改,支持所有 Trojan-Go 扩展特性。

## 使用方法

1. 快速启动服务端和客户端(简易模式)

    - 服务端

        ```shell
        sudo ./trojan-go -server -remote 127.0.0.1:80 -local 0.0.0.0:443 -key ./your_key.key -cert ./your_cert.crt -password your_password
        ```

    - 客户端

        ```shell
        ./trojan-go -client -remote example.com:443 -local 127.0.0.1:1080 -password your_password
        ```

2. 使用配置文件启动客户端 / 服务端 / 透明代理 / 中继(一般模式)

    ```shell
    ./trojan-go -config config.json
    ```

3. 使用 URL 启动客户端(格式参见文档)

    ```shell
    ./trojan-go -url 'trojan-go://password@cloudflare.com/?type=ws&path=%2Fpath&host=your-site.com'
    ```

4. 使用 Docker 部署

    ```shell
    docker run \
        --name trojan-go \
        -d \
        -v /etc/trojan-go/:/etc/trojan-go \
        --network host \
        p4gefau1t/trojan-go
    ```

   或者

    ```shell
    docker run \
        --name trojan-go \
        -d \
        -v /path/to/host/config:/path/in/container \
        --network host \
        p4gefau1t/trojan-go \
        /path/in/container/config.json
    ```

## 特性

一般情况下,Trojan-Go 和 Trojan 是互相兼容的,但一旦使用下面介绍的扩展特性(如多路复用、Websocket 等),则无法兼容。

### 移植性

编译得到的 Trojan-Go 单个可执行文件不依赖其他组件。同时,你可以很方便地编译(或交叉编译) Trojan-Go,然后在你的服务器、PC、树莓派,甚至路由器上部署;可以方便地使用 build tag 删减模块,以缩小可执行文件体积。

例如,交叉编译一个可在 mips 处理器、Linux 操作系统上运行的、只有客户端功能的 Trojan-Go,只需执行下面的命令,得到的可执行文件可以直接在目标平台运行:

```shell
CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -tags "client" -trimpath -ldflags "-s -w -buildid="
```

完整的 tag 说明参见 [Trojan-Go 文档](https://p4gefau1t.github.io/trojan-go)。

### 易用

配置文件格式与原版 Trojan 兼容,但做了大幅简化,未指定的字段会被赋予默认值,由此可以更方便地部署服务端和客户端。以下是一个简单例子,完整的配置文件可以参见[这里](https://p4gefau1t.github.io/trojan-go)。

服务端配置文件 `server.json`:

```json
{
  "run_type": "server",
  "local_addr": "0.0.0.0",
  "local_port": 443,
  "remote_addr": "127.0.0.1",
  "remote_port": 80,
  "password": ["your_awesome_password"],
  "ssl": {
    "cert": "your_cert.crt",
    "key": "your_key.key",
    "sni": "www.your-awesome-domain-name.com"
  }
}
```

客户端配置文件 `client.json`:

```json
{
  "run_type": "client",
  "local_addr": "127.0.0.1",
  "local_port": 1080,
  "remote_addr": "www.your-awesome-domain-name.com",
  "remote_port": 443,
  "password": ["your_awesome_password"]
}
```

可以使用更简明易读的 YAML 语法进行配置。以下是一个客户端的例子,与上面的 `client.json` 等价:

客户端配置文件 `client.yaml`:

```yaml
run-type: client
local-addr: 127.0.0.1
local-port: 1080
remote-addr: www.your-awesome-domain_name.com
remote-port: 443
password:
  - your_awesome_password
```

### WebSocket

Trojan-Go 支持使用 TLS + Websocket 承载 Trojan 协议,使得利用 CDN 进行流量中转成为可能。

服务端和客户端配置文件中同时添加 `websocket` 选项即可启用 Websocket 支持,例如

```json
"websocket": {
    "enabled": true,
    "path": "/your-websocket-path",
    "hostname": "www.your-awesome-domain-name.com"
}
```

完整的选项说明参见 [Trojan-Go 文档](https://p4gefau1t.github.io/trojan-go)。

可以省略 `hostname`, 但服务端和客户端的 `path` 必须一致。服务端开启 Websocket 支持后,可以同时支持 Websocket 和一般 Trojan 流量。未配置 Websocket 选项的客户端依然可以正常使用。

由于 Trojan 并不支持 Websocket,因此,虽然开启了 Websocket 支持的 Trojan-Go 服务端可以兼容所有客户端,但如果要使用 Websocket 承载流量,请确保双方都使用 Trojan-Go。

### 多路复用

在很差的网络条件下,一次 TLS 握手可能会花费很多时间。Trojan-Go 支持多路复用(基于 [smux](https://github.com/xtaci/smux)),通过一条 TLS 隧道连接承载多条 TCP 连接的方式,减少 TCP 和 TLS 握手带来的延迟,以期提升高并发情景下的性能。

> 启用多路复用并不能提高测速得到的链路速度,但能降低延迟、提升大量并发请求时的网络体验,例如浏览含有大量图片的网页等。

你可以通过设置客户端的 `mux` 选项 `enabled` 字段启用它:

```json
"mux": {
    "enabled": true
}
```

只需开启客户端 mux 配置即可,服务端会自动检测是否启用多路复用并提供支持。完整的选项说明参见 [Trojan-Go 文档](https://p4gefau1t.github.io/trojan-go)。

### 路由模块

Trojan-Go 客户端内建一个简单实用的路由模块,以方便实现国内直连、海外代理等自定义路由功能。

路由策略有三种:

- `Proxy` 代理:将请求通过 TLS 隧道进行代理,由 Trojan 服务端与目的地址进行连接。
- `Bypass` 绕过:直接使用本地设备与目的地址进行连接。
- `Block` 封锁:不发送请求,直接关闭连接。

要激活路由模块,请在配置文件中添加 `router` 选项,并设置 `enabled` 字段为 `true`:

```json
"router": {
    "enabled": true,
    "bypass": [
        "geoip:cn",
        "geoip:private",
        "full:localhost"
    ],
    "block": [
        "cidr:192.168.1.1/24",
    ],
    "proxy": [
        "domain:google.com",
    ],
    "default_policy": "proxy"
}
```

完整的选项说明参见 [Trojan-Go 文档](https://p4gefau1t.github.io/trojan-go)。

### AEAD 加密

Trojan-Go 支持基于 Shadowsocks AEAD 对 Trojan 协议流量进行二次加密,以保证 Websocket 传输流量无法被不可信的 CDN 识别和审查:

```json
"shadowsocks": {
    "enabled": true,
    "password": "my-password"
}
```

如需开启,服务端和客户端必须同时开启并保证密码一致。

### 传输层插件

Trojan-Go 支持可插拔的传输层插件,并支持 Shadowsocks [SIP003](https://shadowsocks.org/en/wiki/Plugin.html) 标准的混淆插件。下面是使用 `v2ray-plugin` 的一个例子:

> **此配置并不安全,仅作为演示**

服务端配置:

```json
"transport_plugin": {
    "enabled": true,
    "type": "shadowsocks",
    "command": "./v2ray-plugin",
    "arg": ["-server", "-host", "www.baidu.com"]
}
```

客户端配置:

```json
"transport_plugin": {
    "enabled": true,
    "type": "shadowsocks",
    "command": "./v2ray-plugin",
    "arg": ["-host", "www.baidu.com"]
}
```

完整的选项说明参见 [Trojan-Go 文档](https://p4gefau1t.github.io/trojan-go)。

## 构建

> 请确保 Go 版本 >= 1.14

使用 `make` 进行编译:

```shell
git clone https://github.com/p4gefau1t/trojan-go.git
cd trojan-go
make
make install #安装systemd服务等,可选
```

或者使用 Go 自行编译:

```shell
go build -tags "full"
```

Go 支持通过设置环境变量进行交叉编译,例如:

编译适用于 64 位 Windows 操作系统的可执行文件:

```shell
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -tags "full"
```

编译适用于 Apple Silicon 的可执行文件:

```shell
CGO_ENABLED=0 GOOS=macos GOARCH=arm64 go build -tags "full"
```

编译适用于 64 位 Linux 操作系统的可执行文件:

```shell
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags "full"
```

## 致谢

- [Trojan](https://github.com/trojan-gfw/trojan)
- [V2Fly](https://github.com/v2fly)
- [utls](https://github.com/refraction-networking/utls)
- [smux](https://github.com/xtaci/smux)
- [go-tproxy](https://github.com/LiamHaworth/go-tproxy)

## Stargazers over time

[![Stargazers over time](https://starchart.cc/p4gefau1t/trojan-go.svg)](https://starchart.cc/p4gefau1t/trojan-go)


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

import (
	"context"

	"github.com/p4gefau1t/trojan-go/log"
	"github.com/p4gefau1t/trojan-go/statistic"
)

type Handler func(ctx context.Context, auth statistic.Authenticator) error

var handlers = make(map[string]Handler)

func RegisterHandler(name string, handler Handler) {
	handlers[name] = handler
}

func RunService(ctx context.Context, name string, auth statistic.Authenticator) error {
	if h, ok := handlers[name]; ok {
		log.Debug("api handler found", name)
		return h(ctx, auth)
	}
	log.Debug("api handler not found", name)
	return nil
}


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

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"io"

	"google.golang.org/grpc"

	"github.com/p4gefau1t/trojan-go/api/service"
	"github.com/p4gefau1t/trojan-go/common"
	"github.com/p4gefau1t/trojan-go/log"
	"github.com/p4gefau1t/trojan-go/option"
)

type apiController struct {
	address *string
	key     *string
	hash    *string
	cert    *string

	cmd                *string
	password           *string
	add                *bool
	delete             *bool
	modify             *bool
	list               *bool
	uploadSpeedLimit   *int
	downloadSpeedLimit *int
	ipLimit            *int
	ctx                context.Context
}

func (apiController) Name() string {
	return "api"
}

func (o *apiController) listUsers(apiClient service.TrojanServerServiceClient) error {
	stream, err := apiClient.ListUsers(o.ctx, &service.ListUsersRequest{})
	if err != nil {
		return err
	}
	defer stream.CloseSend()
	result := []*service.ListUsersResponse{}
	for {
		resp, err := stream.Recv()
		if err != nil {
			if err == io.EOF {
				break
			}
			return err
		}
		result = append(result, resp)
	}
	data, err := json.Marshal(result)
	common.Must(err)
	fmt.Println(string(data))
	return nil
}

func (o *apiController) getUsers(apiClient service.TrojanServerServiceClient) error {
	stream, err := apiClient.GetUsers(o.ctx)
	if err != nil {
		return err
	}
	defer stream.CloseSend()
	err = stream.Send(&service.GetUsersRequest{
		User: &service.User{
			Password: *o.password,
			Hash:     *o.hash,
		},
	})
	if err != nil {
		return err
	}
	resp, err := stream.Recv()
	if err != nil {
		return err
	}
	data, err := json.Marshal(resp)
	common.Must(err)
	fmt.Print(string(data))
	return nil
}

func (o *apiController) setUsers(apiClient service.TrojanServerServiceClient) error {
	stream, err := apiClient.SetUsers(o.ctx)
	if err != nil {
		return err
	}
	defer stream.CloseSend()

	req := &service.SetUsersRequest{
		Status: &service.UserStatus{
			User: &service.User{
				Password: *o.password,
				Hash:     *o.hash,
			},
			IpLimit: int32(*o.ipLimit),
			SpeedLimit: &service.Speed{
				UploadSpeed:   uint64(*o.uploadSpeedLimit),
				DownloadSpeed: uint64(*o.downloadSpeedLimit),
			},
		},
	}

	switch {
	case *o.add:
		req.Operation = service.SetUsersRequest_Add
	case *o.modify:
		req.Operation = service.SetUsersRequest_Modify
	case *o.delete:
		req.Operation = service.SetUsersRequest_Delete
	default:
		return common.NewError("Invalid operation")
	}

	err = stream.Send(req)
	if err != nil {
		return err
	}
	resp, err := stream.Recv()
	if err != nil {
		return err
	}
	if resp.Success {
		fmt.Println("Done")
	} else {
		fmt.Println("Failed: " + resp.Info)
	}
	return nil
}

func (o *apiController) Handle() error {
	if *o.cmd == "" {
		return common.NewError("")
	}
	conn, err := grpc.Dial(*o.address, grpc.WithInsecure())
	if err != nil {
		log.Error(err)
		return nil
	}
	defer conn.Close()
	apiClient := service.NewTrojanServerServiceClient(conn)
	switch *o.cmd {
	case "list":
		err := o.listUsers(apiClient)
		if err != nil {
			log.Error(err)
		}
	case "get":
		err := o.getUsers(apiClient)
		if err != nil {
			log.Error(err)
		}
	case "set":
		err := o.setUsers(apiClient)
		if err != nil {
			log.Error(err)
		}
	default:
		log.Error("unknown command " + *o.cmd)
	}
	return nil
}

func (o *apiController) Priority() int {
	return 50
}

func init() {
	option.RegisterHandler(&apiController{
		cmd:                flag.String("api", "", "Connect to a Trojan-Go API service. \"-api add/get/list\""),
		address:            flag.String("api-addr", "127.0.0.1:10000", "Address of Trojan-Go API service"),
		password:           flag.String("target-password", "", "Password of the target user"),
		hash:               flag.String("target-hash", "", "Hash of the target user"),
		add:                flag.Bool("add-profile", false, "Add a new profile with API"),
		delete:             flag.Bool("delete-profile", false, "Delete an existing profile with API"),
		modify:             flag.Bool("modify-profile", false, "Modify an existing profile with API"),
		uploadSpeedLimit:   flag.Int("upload-speed-limit", 0, "Limit the upload speed with API"),
		downloadSpeedLimit: flag.Int("download-speed-limit", 0, "Limit the download speed with API"),
		ipLimit:            flag.Int("ip-limit", 0, "Limit the number of IP with API"),
		ctx:                context.Background(),
	})
}


================================================
FILE: api/service/api.pb.go
================================================
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.27.1
// 	protoc        v3.17.3
// source: api.proto

package service

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

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

type SetUsersRequest_Operation int32

const (
	SetUsersRequest_Add    SetUsersRequest_Operation = 0
	SetUsersRequest_Delete SetUsersRequest_Operation = 1
	SetUsersRequest_Modify SetUsersRequest_Operation = 2
)

// Enum value maps for SetUsersRequest_Operation.
var (
	SetUsersRequest_Operation_name = map[int32]string{
		0: "Add",
		1: "Delete",
		2: "Modify",
	}
	SetUsersRequest_Operation_value = map[string]int32{
		"Add":    0,
		"Delete": 1,
		"Modify": 2,
	}
)

func (x SetUsersRequest_Operation) Enum() *SetUsersRequest_Operation {
	p := new(SetUsersRequest_Operation)
	*p = x
	return p
}

func (x SetUsersRequest_Operation) String() string {
	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}

func (SetUsersRequest_Operation) Descriptor() protoreflect.EnumDescriptor {
	return file_api_proto_enumTypes[0].Descriptor()
}

func (SetUsersRequest_Operation) Type() protoreflect.EnumType {
	return &file_api_proto_enumTypes[0]
}

func (x SetUsersRequest_Operation) Number() protoreflect.EnumNumber {
	return protoreflect.EnumNumber(x)
}

// Deprecated: Use SetUsersRequest_Operation.Descriptor instead.
func (SetUsersRequest_Operation) EnumDescriptor() ([]byte, []int) {
	return file_api_proto_rawDescGZIP(), []int{10, 0}
}

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

	UploadTraffic   uint64 `protobuf:"varint,1,opt,name=upload_traffic,json=uploadTraffic,proto3" json:"upload_traffic,omitempty"`
	DownloadTraffic uint64 `protobuf:"varint,2,opt,name=download_traffic,json=downloadTraffic,proto3" json:"download_traffic,omitempty"`
}

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

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

func (*Traffic) ProtoMessage() {}

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

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

func (x *Traffic) GetUploadTraffic() uint64 {
	if x != nil {
		return x.UploadTraffic
	}
	return 0
}

func (x *Traffic) GetDownloadTraffic() uint64 {
	if x != nil {
		return x.DownloadTraffic
	}
	return 0
}

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

	UploadSpeed   uint64 `protobuf:"varint,1,opt,name=upload_speed,json=uploadSpeed,proto3" json:"upload_speed,omitempty"`
	DownloadSpeed uint64 `protobuf:"varint,2,opt,name=download_speed,json=downloadSpeed,proto3" json:"download_speed,omitempty"`
}

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

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

func (*Speed) ProtoMessage() {}

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

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

func (x *Speed) GetUploadSpeed() uint64 {
	if x != nil {
		return x.UploadSpeed
	}
	return 0
}

func (x *Speed) GetDownloadSpeed() uint64 {
	if x != nil {
		return x.DownloadSpeed
	}
	return 0
}

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

	Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"`
	Hash     string `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"`
}

func (x *User) Reset() {
	*x = User{}
	if protoimpl.UnsafeEnabled {
		mi := &file_api_proto_msgTypes[2]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

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

func (*User) ProtoMessage() {}

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

// Deprecated: Use User.ProtoReflect.Descriptor instead.
func (*User) Descriptor() ([]byte, []int) {
	return file_api_proto_rawDescGZIP(), []int{2}
}

func (x *User) GetPassword() string {
	if x != nil {
		return x.Password
	}
	return ""
}

func (x *User) GetHash() string {
	if x != nil {
		return x.Hash
	}
	return ""
}

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

	User         *User    `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
	TrafficTotal *Traffic `protobuf:"bytes,2,opt,name=traffic_total,json=trafficTotal,proto3" json:"traffic_total,omitempty"`
	SpeedCurrent *Speed   `protobuf:"bytes,3,opt,name=speed_current,json=speedCurrent,proto3" json:"speed_current,omitempty"`
	SpeedLimit   *Speed   `protobuf:"bytes,4,opt,name=speed_limit,json=speedLimit,proto3" json:"speed_limit,omitempty"`
	IpCurrent    int32    `protobuf:"varint,5,opt,name=ip_current,json=ipCurrent,proto3" json:"ip_current,omitempty"`
	IpLimit      int32    `protobuf:"varint,6,opt,name=ip_limit,json=ipLimit,proto3" json:"ip_limit,omitempty"`
}

func (x *UserStatus) Reset() {
	*x = UserStatus{}
	if protoimpl.UnsafeEnabled {
		mi := &file_api_proto_msgTypes[3]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

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

func (*UserStatus) ProtoMessage() {}

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

// Deprecated: Use UserStatus.ProtoReflect.Descriptor instead.
func (*UserStatus) Descriptor() ([]byte, []int) {
	return file_api_proto_rawDescGZIP(), []int{3}
}

func (x *UserStatus) GetUser() *User {
	if x != nil {
		return x.User
	}
	return nil
}

func (x *UserStatus) GetTrafficTotal() *Traffic {
	if x != nil {
		return x.TrafficTotal
	}
	return nil
}

func (x *UserStatus) GetSpeedCurrent() *Speed {
	if x != nil {
		return x.SpeedCurrent
	}
	return nil
}

func (x *UserStatus) GetSpeedLimit() *Speed {
	if x != nil {
		return x.SpeedLimit
	}
	return nil
}

func (x *UserStatus) GetIpCurrent() int32 {
	if x != nil {
		return x.IpCurrent
	}
	return 0
}

func (x *UserStatus) GetIpLimit() int32 {
	if x != nil {
		return x.IpLimit
	}
	return 0
}

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

	User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
}

func (x *GetTrafficRequest) Reset() {
	*x = GetTrafficRequest{}
	if protoimpl.UnsafeEnabled {
		mi := &file_api_proto_msgTypes[4]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

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

func (*GetTrafficRequest) ProtoMessage() {}

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

// Deprecated: Use GetTrafficRequest.ProtoReflect.Descriptor instead.
func (*GetTrafficRequest) Descriptor() ([]byte, []int) {
	return file_api_proto_rawDescGZIP(), []int{4}
}

func (x *GetTrafficRequest) GetUser() *User {
	if x != nil {
		return x.User
	}
	return nil
}

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

	Success      bool     `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
	Info         string   `protobuf:"bytes,2,opt,name=info,proto3" json:"info,omitempty"`
	TrafficTotal *Traffic `protobuf:"bytes,3,opt,name=traffic_total,json=trafficTotal,proto3" json:"traffic_total,omitempty"`
	SpeedCurrent *Speed   `protobuf:"bytes,4,opt,name=speed_current,json=speedCurrent,proto3" json:"speed_current,omitempty"`
}

func (x *GetTrafficResponse) Reset() {
	*x = GetTrafficResponse{}
	if protoimpl.UnsafeEnabled {
		mi := &file_api_proto_msgTypes[5]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

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

func (*GetTrafficResponse) ProtoMessage() {}

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

// Deprecated: Use GetTrafficResponse.ProtoReflect.Descriptor instead.
func (*GetTrafficResponse) Descriptor() ([]byte, []int) {
	return file_api_proto_rawDescGZIP(), []int{5}
}

func (x *GetTrafficResponse) GetSuccess() bool {
	if x != nil {
		return x.Success
	}
	return false
}

func (x *GetTrafficResponse) GetInfo() string {
	if x != nil {
		return x.Info
	}
	return ""
}

func (x *GetTrafficResponse) GetTrafficTotal() *Traffic {
	if x != nil {
		return x.TrafficTotal
	}
	return nil
}

func (x *GetTrafficResponse) GetSpeedCurrent() *Speed {
	if x != nil {
		return x.SpeedCurrent
	}
	return nil
}

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

func (x *ListUsersRequest) Reset() {
	*x = ListUsersRequest{}
	if protoimpl.UnsafeEnabled {
		mi := &file_api_proto_msgTypes[6]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

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

func (*ListUsersRequest) ProtoMessage() {}

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

// Deprecated: Use ListUsersRequest.ProtoReflect.Descriptor instead.
func (*ListUsersRequest) Descriptor() ([]byte, []int) {
	return file_api_proto_rawDescGZIP(), []int{6}
}

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

	Status *UserStatus `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"`
}

func (x *ListUsersResponse) Reset() {
	*x = ListUsersResponse{}
	if protoimpl.UnsafeEnabled {
		mi := &file_api_proto_msgTypes[7]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

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

func (*ListUsersResponse) ProtoMessage() {}

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

// Deprecated: Use ListUsersResponse.ProtoReflect.Descriptor instead.
func (*ListUsersResponse) Descriptor() ([]byte, []int) {
	return file_api_proto_rawDescGZIP(), []int{7}
}

func (x *ListUsersResponse) GetStatus() *UserStatus {
	if x != nil {
		return x.Status
	}
	return nil
}

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

	User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
}

func (x *GetUsersRequest) Reset() {
	*x = GetUsersRequest{}
	if protoimpl.UnsafeEnabled {
		mi := &file_api_proto_msgTypes[8]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

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

func (*GetUsersRequest) ProtoMessage() {}

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

// Deprecated: Use GetUsersRequest.ProtoReflect.Descriptor instead.
func (*GetUsersRequest) Descriptor() ([]byte, []int) {
	return file_api_proto_rawDescGZIP(), []int{8}
}

func (x *GetUsersRequest) GetUser() *User {
	if x != nil {
		return x.User
	}
	return nil
}

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

	Success bool        `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
	Info    string      `protobuf:"bytes,2,opt,name=info,proto3" json:"info,omitempty"`
	Status  *UserStatus `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"`
}

func (x *GetUsersResponse) Reset() {
	*x = GetUsersResponse{}
	if protoimpl.UnsafeEnabled {
		mi := &file_api_proto_msgTypes[9]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

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

func (*GetUsersResponse) ProtoMessage() {}

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

// Deprecated: Use GetUsersResponse.ProtoReflect.Descriptor instead.
func (*GetUsersResponse) Descriptor() ([]byte, []int) {
	return file_api_proto_rawDescGZIP(), []int{9}
}

func (x *GetUsersResponse) GetSuccess() bool {
	if x != nil {
		return x.Success
	}
	return false
}

func (x *GetUsersResponse) GetInfo() string {
	if x != nil {
		return x.Info
	}
	return ""
}

func (x *GetUsersResponse) GetStatus() *UserStatus {
	if x != nil {
		return x.Status
	}
	return nil
}

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

	Status    *UserStatus               `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"`
	Operation SetUsersRequest_Operation `protobuf:"varint,2,opt,name=operation,proto3,enum=trojan.api.SetUsersRequest_Operation" json:"operation,omitempty"`
}

func (x *SetUsersRequest) Reset() {
	*x = SetUsersRequest{}
	if protoimpl.UnsafeEnabled {
		mi := &file_api_proto_msgTypes[10]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

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

func (*SetUsersRequest) ProtoMessage() {}

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

// Deprecated: Use SetUsersRequest.ProtoReflect.Descriptor instead.
func (*SetUsersRequest) Descriptor() ([]byte, []int) {
	return file_api_proto_rawDescGZIP(), []int{10}
}

func (x *SetUsersRequest) GetStatus() *UserStatus {
	if x != nil {
		return x.Status
	}
	return nil
}

func (x *SetUsersRequest) GetOperation() SetUsersRequest_Operation {
	if x != nil {
		return x.Operation
	}
	return SetUsersRequest_Add
}

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

	Success bool   `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
	Info    string `protobuf:"bytes,2,opt,name=info,proto3" json:"info,omitempty"`
}

func (x *SetUsersResponse) Reset() {
	*x = SetUsersResponse{}
	if protoimpl.UnsafeEnabled {
		mi := &file_api_proto_msgTypes[11]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

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

func (*SetUsersResponse) ProtoMessage() {}

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

// Deprecated: Use SetUsersResponse.ProtoReflect.Descriptor instead.
func (*SetUsersResponse) Descriptor() ([]byte, []int) {
	return file_api_proto_rawDescGZIP(), []int{11}
}

func (x *SetUsersResponse) GetSuccess() bool {
	if x != nil {
		return x.Success
	}
	return false
}

func (x *SetUsersResponse) GetInfo() string {
	if x != nil {
		return x.Info
	}
	return ""
}

var File_api_proto protoreflect.FileDescriptor

var file_api_proto_rawDesc = []byte{
	0x0a, 0x09, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x74, 0x72, 0x6f,
	0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x22, 0x5b, 0x0a, 0x07, 0x54, 0x72, 0x61, 0x66, 0x66,
	0x69, 0x63, 0x12, 0x25, 0x0a, 0x0e, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x74, 0x72, 0x61,
	0x66, 0x66, 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x75, 0x70, 0x6c, 0x6f,
	0x61, 0x64, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x6f, 0x77,
	0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x18, 0x02, 0x20,
	0x01, 0x28, 0x04, 0x52, 0x0f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x72, 0x61,
	0x66, 0x66, 0x69, 0x63, 0x22, 0x51, 0x0a, 0x05, 0x53, 0x70, 0x65, 0x65, 0x64, 0x12, 0x21, 0x0a,
	0x0c, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64, 0x18, 0x01, 0x20,
	0x01, 0x28, 0x04, 0x52, 0x0b, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x70, 0x65, 0x65, 0x64,
	0x12, 0x25, 0x0a, 0x0e, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x70, 0x65,
	0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f,
	0x61, 0x64, 0x53, 0x70, 0x65, 0x65, 0x64, 0x22, 0x36, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12,
	0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
	0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x68,
	0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22,
	0x92, 0x02, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x24,
	0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74,
	0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04,
	0x75, 0x73, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x5f,
	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72,
	0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63,
	0x52, 0x0c, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x36,
	0x0a, 0x0d, 0x73, 0x70, 0x65, 0x65, 0x64, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x18,
	0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61,
	0x70, 0x69, 0x2e, 0x53, 0x70, 0x65, 0x65, 0x64, 0x52, 0x0c, 0x73, 0x70, 0x65, 0x65, 0x64, 0x43,
	0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x65, 0x64, 0x5f,
	0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x72,
	0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x70, 0x65, 0x65, 0x64, 0x52, 0x0a,
	0x73, 0x70, 0x65, 0x65, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x70,
	0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09,
	0x69, 0x70, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x70, 0x5f,
	0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x69, 0x70, 0x4c,
	0x69, 0x6d, 0x69, 0x74, 0x22, 0x39, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x66, 0x66,
	0x69, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x04, 0x75, 0x73, 0x65,
	0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e,
	0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22,
	0xb4, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x52, 0x65,
	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73,
	0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73,
	0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
	0x69, 0x6e, 0x66, 0x6f, 0x12, 0x38, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x5f,
	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72,
	0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63,
	0x52, 0x0c, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x36,
	0x0a, 0x0d, 0x73, 0x70, 0x65, 0x65, 0x64, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x18,
	0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61,
	0x70, 0x69, 0x2e, 0x53, 0x70, 0x65, 0x65, 0x64, 0x52, 0x0c, 0x73, 0x70, 0x65, 0x65, 0x64, 0x43,
	0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x22, 0x12, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73,
	0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, 0x11, 0x4c, 0x69,
	0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
	0x2e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
	0x16, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x73, 0x65,
	0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22,
	0x37, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
	0x73, 0x74, 0x12, 0x24, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
	0x32, 0x10, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x73,
	0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x70, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x55,
	0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07,
	0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73,
	0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02,
	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x74,
	0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x6f,
	0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74,
	0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xb4, 0x01, 0x0a, 0x0f, 0x53,
	0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e,
	0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,
	0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x73, 0x65, 0x72,
	0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x43,
	0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
	0x0e, 0x32, 0x25, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53,
	0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f,
	0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
	0x69, 0x6f, 0x6e, 0x22, 0x2c, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
	0x12, 0x07, 0x0a, 0x03, 0x41, 0x64, 0x64, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x65, 0x6c,
	0x65, 0x74, 0x65, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x10,
	0x02, 0x22, 0x40, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73,
	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73,
	0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12,
	0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69,
	0x6e, 0x66, 0x6f, 0x32, 0x64, 0x0a, 0x13, 0x54, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x43, 0x6c, 0x69,
	0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4d, 0x0a, 0x0a, 0x47, 0x65,
	0x74, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x12, 0x1d, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61,
	0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63,
	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e,
	0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x52,
	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0xfd, 0x01, 0x0a, 0x13, 0x54, 0x72,
	0x6f, 0x6a, 0x61, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
	0x65, 0x12, 0x4c, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1c,
	0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74,
	0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x74,
	0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73,
	0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12,
	0x4b, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x74, 0x72,
	0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72,
	0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61,
	0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65,
	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x4b, 0x0a, 0x08,
	0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61,
	0x6e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65,
	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2e, 0x61,
	0x70, 0x69, 0x2e, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
	0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74,
	0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x34, 0x67, 0x65, 0x66, 0x61, 0x75, 0x31,
	0x74, 0x2f, 0x74, 0x72, 0x6f, 0x6a, 0x61, 0x6e, 0x2d, 0x67, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f,
	0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}

var (
	file_api_proto_rawDescOnce sync.Once
	file_api_proto_rawDescData = file_api_proto_rawDesc
)

func file_api_proto_rawDescGZIP() []byte {
	file_api_proto_rawDescOnce.Do(func() {
		file_api_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_proto_rawDescData)
	})
	return file_api_proto_rawDescData
}

var file_api_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_api_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
var file_api_proto_goTypes = []interface{}{
	(SetUsersRequest_Operation)(0), // 0: trojan.api.SetUsersRequest.Operation
	(*Traffic)(nil),                // 1: trojan.api.Traffic
	(*Speed)(nil),                  // 2: trojan.api.Speed
	(*User)(nil),                   // 3: trojan.api.User
	(*UserStatus)(nil),             // 4: trojan.api.UserStatus
	(*GetTrafficRequest)(nil),      // 5: trojan.api.GetTrafficRequest
	(*GetTrafficResponse)(nil),     // 6: trojan.api.GetTrafficResponse
	(*ListUsersRequest)(nil),       // 7: trojan.api.ListUsersRequest
	(*ListUsersResponse)(nil),      // 8: trojan.api.ListUsersResponse
	(*GetUsersRequest)(nil),        // 9: trojan.api.GetUsersRequest
	(*GetUsersResponse)(nil),       // 10: trojan.api.GetUsersResponse
	(*SetUsersRequest)(nil),        // 11: trojan.api.SetUsersRequest
	(*SetUsersResponse)(nil),       // 12: trojan.api.SetUsersResponse
}
var file_api_proto_depIdxs = []int32{
	3,  // 0: trojan.api.UserStatus.user:type_name -> trojan.api.User
	1,  // 1: trojan.api.UserStatus.traffic_total:type_name -> trojan.api.Traffic
	2,  // 2: trojan.api.UserStatus.speed_current:type_name -> trojan.api.Speed
	2,  // 3: trojan.api.UserStatus.speed_limit:type_name -> trojan.api.Speed
	3,  // 4: trojan.api.GetTrafficRequest.user:type_name -> trojan.api.User
	1,  // 5: trojan.api.GetTrafficResponse.traffic_total:type_name -> trojan.api.Traffic
	2,  // 6: trojan.api.GetTrafficResponse.speed_current:type_name -> trojan.api.Speed
	4,  // 7: trojan.api.ListUsersResponse.status:type_name -> trojan.api.UserStatus
	3,  // 8: trojan.api.GetUsersRequest.user:type_name -> trojan.api.User
	4,  // 9: trojan.api.GetUsersResponse.status:type_name -> trojan.api.UserStatus
	4,  // 10: trojan.api.SetUsersRequest.status:type_name -> trojan.api.UserStatus
	0,  // 11: trojan.api.SetUsersRequest.operation:type_name -> trojan.api.SetUsersRequest.Operation
	5,  // 12: trojan.api.TrojanClientService.GetTraffic:input_type -> trojan.api.GetTrafficRequest
	7,  // 13: trojan.api.TrojanServerService.ListUsers:input_type -> trojan.api.ListUsersRequest
	9,  // 14: trojan.api.TrojanServerService.GetUsers:input_type -> trojan.api.GetUsersRequest
	11, // 15: trojan.api.TrojanServerService.SetUsers:input_type -> trojan.api.SetUsersRequest
	6,  // 16: trojan.api.TrojanClientService.GetTraffic:output_type -> trojan.api.GetTrafficResponse
	8,  // 17: trojan.api.TrojanServerService.ListUsers:output_type -> trojan.api.ListUsersResponse
	10, // 18: trojan.api.TrojanServerService.GetUsers:output_type -> trojan.api.GetUsersResponse
	12, // 19: trojan.api.TrojanServerService.SetUsers:output_type -> trojan.api.SetUsersResponse
	16, // [16:20] is the sub-list for method output_type
	12, // [12:16] is the sub-list for method input_type
	12, // [12:12] is the sub-list for extension type_name
	12, // [12:12] is the sub-list for extension extendee
	0,  // [0:12] is the sub-list for field type_name
}

func init() { file_api_proto_init() }
func file_api_proto_init() {
	if File_api_proto != nil {
		return
	}
	if !protoimpl.UnsafeEnabled {
		file_api_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*Traffic); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_api_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*Speed); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_api_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*User); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_api_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*UserStatus); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_api_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*GetTrafficRequest); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_api_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*GetTrafficResponse); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_api_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*ListUsersRequest); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_api_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*ListUsersResponse); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_api_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*GetUsersRequest); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_api_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*GetUsersResponse); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_api_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*SetUsersRequest); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_api_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*SetUsersResponse); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
	}
	type x struct{}
	out := protoimpl.TypeBuilder{
		File: protoimpl.DescBuilder{
			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
			RawDescriptor: file_api_proto_rawDesc,
			NumEnums:      1,
			NumMessages:   12,
			NumExtensions: 0,
			NumServices:   2,
		},
		GoTypes:           file_api_proto_goTypes,
		DependencyIndexes: file_api_proto_depIdxs,
		EnumInfos:         file_api_proto_enumTypes,
		MessageInfos:      file_api_proto_msgTypes,
	}.Build()
	File_api_proto = out.File
	file_api_proto_rawDesc = nil
	file_api_proto_goTypes = nil
	file_api_proto_depIdxs = nil
}


================================================
FILE: api/service/api.proto
================================================
syntax = "proto3";

package trojan.api;
option go_package = "github.com/p4gefau1t/trojan-go/api/service";

message Traffic {
    uint64 upload_traffic = 1;
    uint64 download_traffic = 2;
}

message Speed {
    uint64 upload_speed = 1;
    uint64 download_speed = 2;
}

message User {
    string password = 1;
    string hash = 2;
}

message UserStatus {
    User user = 1;
    Traffic traffic_total = 2;
    Speed speed_current = 3;
    Speed speed_limit = 4;
    int32 ip_current = 5;
    int32 ip_limit = 6;
}

message GetTrafficRequest {
    User user = 1;
}

message GetTrafficResponse {
    bool success = 1;
    string info = 2;
    Traffic traffic_total = 3;
    Speed speed_current = 4;
}

message ListUsersRequest {

}

message ListUsersResponse {
    UserStatus status = 1;
}

message GetUsersRequest {
    User user = 1;
}

message GetUsersResponse {
    bool success = 1;
    string info = 2;
    UserStatus status = 3;
}

message SetUsersRequest {
    enum Operation {
        Add = 0;
        Delete = 1;
        Modify = 2;
    }
    UserStatus status = 1;
    Operation operation = 2;
}

message SetUsersResponse {
    bool success = 1;
    string info = 2;
}

service TrojanClientService {
    rpc GetTraffic(GetTrafficRequest) returns(GetTrafficResponse){}
}

service TrojanServerService {
    // list all users
    rpc ListUsers(ListUsersRequest) returns(stream ListUsersResponse){}
    // obtain specified user's info
    rpc GetUsers(stream GetUsersRequest) returns(stream GetUsersResponse){}
    // setup existing users' config
    rpc SetUsers(stream SetUsersRequest) returns(stream SetUsersResponse){}
}


================================================
FILE: api/service/api_grpc.pb.go
================================================
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.

package service

import (
	context "context"
	grpc "google.golang.org/grpc"
	codes "google.golang.org/grpc/codes"
	status "google.golang.org/grpc/status"
)

// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7

// TrojanClientServiceClient is the client API for TrojanClientService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type TrojanClientServiceClient interface {
	GetTraffic(ctx context.Context, in *GetTrafficRequest, opts ...grpc.CallOption) (*GetTrafficResponse, error)
}

type trojanClientServiceClient struct {
	cc grpc.ClientConnInterface
}

func NewTrojanClientServiceClient(cc grpc.ClientConnInterface) TrojanClientServiceClient {
	return &trojanClientServiceClient{cc}
}

func (c *trojanClientServiceClient) GetTraffic(ctx context.Context, in *GetTrafficRequest, opts ...grpc.CallOption) (*GetTrafficResponse, error) {
	out := new(GetTrafficResponse)
	err := c.cc.Invoke(ctx, "/trojan.api.TrojanClientService/GetTraffic", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// TrojanClientServiceServer is the server API for TrojanClientService service.
// All implementations must embed UnimplementedTrojanClientServiceServer
// for forward compatibility
type TrojanClientServiceServer interface {
	GetTraffic(context.Context, *GetTrafficRequest) (*GetTrafficResponse, error)
	mustEmbedUnimplementedTrojanClientServiceServer()
}

// UnimplementedTrojanClientServiceServer must be embedded to have forward compatible implementations.
type UnimplementedTrojanClientServiceServer struct {
}

func (UnimplementedTrojanClientServiceServer) GetTraffic(context.Context, *GetTrafficRequest) (*GetTrafficResponse, error) {
	return nil, status.Errorf(codes.Unimplemented, "method GetTraffic not implemented")
}
func (UnimplementedTrojanClientServiceServer) mustEmbedUnimplementedTrojanClientServiceServer() {}

// UnsafeTrojanClientServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to TrojanClientServiceServer will
// result in compilation errors.
type UnsafeTrojanClientServiceServer interface {
	mustEmbedUnimplementedTrojanClientServiceServer()
}

func RegisterTrojanClientServiceServer(s grpc.ServiceRegistrar, srv TrojanClientServiceServer) {
	s.RegisterService(&TrojanClientService_ServiceDesc, srv)
}

func _TrojanClientService_GetTraffic_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
	in := new(GetTrafficRequest)
	if err := dec(in); err != nil {
		return nil, err
	}
	if interceptor == nil {
		return srv.(TrojanClientServiceServer).GetTraffic(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
		Server:     srv,
		FullMethod: "/trojan.api.TrojanClientService/GetTraffic",
	}
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
		return srv.(TrojanClientServiceServer).GetTraffic(ctx, req.(*GetTrafficRequest))
	}
	return interceptor(ctx, in, info, handler)
}

// TrojanClientService_ServiceDesc is the grpc.ServiceDesc for TrojanClientService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var TrojanClientService_ServiceDesc = grpc.ServiceDesc{
	ServiceName: "trojan.api.TrojanClientService",
	HandlerType: (*TrojanClientServiceServer)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "GetTraffic",
			Handler:    _TrojanClientService_GetTraffic_Handler,
		},
	},
	Streams:  []grpc.StreamDesc{},
	Metadata: "api.proto",
}

// TrojanServerServiceClient is the client API for TrojanServerService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type TrojanServerServiceClient interface {
	// list all users
	ListUsers(ctx context.Context, in *ListUsersRequest, opts ...grpc.CallOption) (TrojanServerService_ListUsersClient, error)
	// obtain specified user's info
	GetUsers(ctx context.Context, opts ...grpc.CallOption) (TrojanServerService_GetUsersClient, error)
	// setup existing users' config
	SetUsers(ctx context.Context, opts ...grpc.CallOption) (TrojanServerService_SetUsersClient, error)
}

type trojanServerServiceClient struct {
	cc grpc.ClientConnInterface
}

func NewTrojanServerServiceClient(cc grpc.ClientConnInterface) TrojanServerServiceClient {
	return &trojanServerServiceClient{cc}
}

func (c *trojanServerServiceClient) ListUsers(ctx context.Context, in *ListUsersRequest, opts ...grpc.CallOption) (TrojanServerService_ListUsersClient, error) {
	stream, err := c.cc.NewStream(ctx, &TrojanServerService_ServiceDesc.Streams[0], "/trojan.api.TrojanServerService/ListUsers", opts...)
	if err != nil {
		return nil, err
	}
	x := &trojanServerServiceListUsersClient{stream}
	if err := x.ClientStream.SendMsg(in); err != nil {
		return nil, err
	}
	if err := x.ClientStream.CloseSend(); err != nil {
		return nil, err
	}
	return x, nil
}

type TrojanServerService_ListUsersClient interface {
	Recv() (*ListUsersResponse, error)
	grpc.ClientStream
}

type trojanServerServiceListUsersClient struct {
	grpc.ClientStream
}

func (x *trojanServerServiceListUsersClient) Recv() (*ListUsersResponse, error) {
	m := new(ListUsersResponse)
	if err := x.ClientStream.RecvMsg(m); err != nil {
		return nil, err
	}
	return m, nil
}

func (c *trojanServerServiceClient) GetUsers(ctx context.Context, opts ...grpc.CallOption) (TrojanServerService_GetUsersClient, error) {
	stream, err := c.cc.NewStream(ctx, &TrojanServerService_ServiceDesc.Streams[1], "/trojan.api.TrojanServerService/GetUsers", opts...)
	if err != nil {
		return nil, err
	}
	x := &trojanServerServiceGetUsersClient{stream}
	return x, nil
}

type TrojanServerService_GetUsersClient interface {
	Send(*GetUsersRequest) error
	Recv() (*GetUsersResponse, error)
	grpc.ClientStream
}

type trojanServerServiceGetUsersClient struct {
	grpc.ClientStream
}

func (x *trojanServerServiceGetUsersClient) Send(m *GetUsersRequest) error {
	return x.ClientStream.SendMsg(m)
}

func (x *trojanServerServiceGetUsersClient) Recv() (*GetUsersResponse, error) {
	m := new(GetUsersResponse)
	if err := x.ClientStream.RecvMsg(m); err != nil {
		return nil, err
	}
	return m, nil
}

func (c *trojanServerServiceClient) SetUsers(ctx context.Context, opts ...grpc.CallOption) (TrojanServerService_SetUsersClient, error) {
	stream, err := c.cc.NewStream(ctx, &TrojanServerService_ServiceDesc.Streams[2], "/trojan.api.TrojanServerService/SetUsers", opts...)
	if err != nil {
		return nil, err
	}
	x := &trojanServerServiceSetUsersClient{stream}
	return x, nil
}

type TrojanServerService_SetUsersClient interface {
	Send(*SetUsersRequest) error
	Recv() (*SetUsersResponse, error)
	grpc.ClientStream
}

type trojanServerServiceSetUsersClient struct {
	grpc.ClientStream
}

func (x *trojanServerServiceSetUsersClient) Send(m *SetUsersRequest) error {
	return x.ClientStream.SendMsg(m)
}

func (x *trojanServerServiceSetUsersClient) Recv() (*SetUsersResponse, error) {
	m := new(SetUsersResponse)
	if err := x.ClientStream.RecvMsg(m); err != nil {
		return nil, err
	}
	return m, nil
}

// TrojanServerServiceServer is the server API for TrojanServerService service.
// All implementations must embed UnimplementedTrojanServerServiceServer
// for forward compatibility
type TrojanServerServiceServer interface {
	// list all users
	ListUsers(*ListUsersRequest, TrojanServerService_ListUsersServer) error
	// obtain specified user's info
	GetUsers(TrojanServerService_GetUsersServer) error
	// setup existing users' config
	SetUsers(TrojanServerService_SetUsersServer) error
	mustEmbedUnimplementedTrojanServerServiceServer()
}

// UnimplementedTrojanServerServiceServer must be embedded to have forward compatible implementations.
type UnimplementedTrojanServerServiceServer struct {
}

func (UnimplementedTrojanServerServiceServer) ListUsers(*ListUsersRequest, TrojanServerService_ListUsersServer) error {
	return status.Errorf(codes.Unimplemented, "method ListUsers not implemented")
}
func (UnimplementedTrojanServerServiceServer) GetUsers(TrojanServerService_GetUsersServer) error {
	return status.Errorf(codes.Unimplemented, "method GetUsers not implemented")
}
func (UnimplementedTrojanServerServiceServer) SetUsers(TrojanServerService_SetUsersServer) error {
	return status.Errorf(codes.Unimplemented, "method SetUsers not implemented")
}
func (UnimplementedTrojanServerServiceServer) mustEmbedUnimplementedTrojanServerServiceServer() {}

// UnsafeTrojanServerServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to TrojanServerServiceServer will
// result in compilation errors.
type UnsafeTrojanServerServiceServer interface {
	mustEmbedUnimplementedTrojanServerServiceServer()
}

func RegisterTrojanServerServiceServer(s grpc.ServiceRegistrar, srv TrojanServerServiceServer) {
	s.RegisterService(&TrojanServerService_ServiceDesc, srv)
}

func _TrojanServerService_ListUsers_Handler(srv interface{}, stream grpc.ServerStream) error {
	m := new(ListUsersRequest)
	if err := stream.RecvMsg(m); err != nil {
		return err
	}
	return srv.(TrojanServerServiceServer).ListUsers(m, &trojanServerServiceListUsersServer{stream})
}

type TrojanServerService_ListUsersServer interface {
	Send(*ListUsersResponse) error
	grpc.ServerStream
}

type trojanServerServiceListUsersServer struct {
	grpc.ServerStream
}

func (x *trojanServerServiceListUsersServer) Send(m *ListUsersResponse) error {
	return x.ServerStream.SendMsg(m)
}

func _TrojanServerService_GetUsers_Handler(srv interface{}, stream grpc.ServerStream) error {
	return srv.(TrojanServerServiceServer).GetUsers(&trojanServerServiceGetUsersServer{stream})
}

type TrojanServerService_GetUsersServer interface {
	Send(*GetUsersResponse) error
	Recv() (*GetUsersRequest, error)
	grpc.ServerStream
}

type trojanServerServiceGetUsersServer struct {
	grpc.ServerStream
}

func (x *trojanServerServiceGetUsersServer) Send(m *GetUsersResponse) error {
	return x.ServerStream.SendMsg(m)
}

func (x *trojanServerServiceGetUsersServer) Recv() (*GetUsersRequest, error) {
	m := new(GetUsersRequest)
	if err := x.ServerStream.RecvMsg(m); err != nil {
		return nil, err
	}
	return m, nil
}

func _TrojanServerService_SetUsers_Handler(srv interface{}, stream grpc.ServerStream) error {
	return srv.(TrojanServerServiceServer).SetUsers(&trojanServerServiceSetUsersServer{stream})
}

type TrojanServerService_SetUsersServer interface {
	Send(*SetUsersResponse) error
	Recv() (*SetUsersRequest, error)
	grpc.ServerStream
}

type trojanServerServiceSetUsersServer struct {
	grpc.ServerStream
}

func (x *trojanServerServiceSetUsersServer) Send(m *SetUsersResponse) error {
	return x.ServerStream.SendMsg(m)
}

func (x *trojanServerServiceSetUsersServer) Recv() (*SetUsersRequest, error) {
	m := new(SetUsersRequest)
	if err := x.ServerStream.RecvMsg(m); err != nil {
		return nil, err
	}
	return m, nil
}

// TrojanServerService_ServiceDesc is the grpc.ServiceDesc for TrojanServerService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var TrojanServerService_ServiceDesc = grpc.ServiceDesc{
	ServiceName: "trojan.api.TrojanServerService",
	HandlerType: (*TrojanServerServiceServer)(nil),
	Methods:     []grpc.MethodDesc{},
	Streams: []grpc.StreamDesc{
		{
			StreamName:    "ListUsers",
			Handler:       _TrojanServerService_ListUsers_Handler,
			ServerStreams: true,
		},
		{
			StreamName:    "GetUsers",
			Handler:       _TrojanServerService_GetUsers_Handler,
			ServerStreams: true,
			ClientStreams: true,
		},
		{
			StreamName:    "SetUsers",
			Handler:       _TrojanServerService_SetUsers_Handler,
			ServerStreams: true,
			ClientStreams: true,
		},
	},
	Metadata: "api.proto",
}


================================================
FILE: api/service/client.go
================================================
package service

import (
	"context"
	"net"

	"github.com/p4gefau1t/trojan-go/api"
	"github.com/p4gefau1t/trojan-go/common"
	"github.com/p4gefau1t/trojan-go/config"
	"github.com/p4gefau1t/trojan-go/log"
	"github.com/p4gefau1t/trojan-go/statistic"
	"github.com/p4gefau1t/trojan-go/tunnel/trojan"
)

type ClientAPI struct {
	TrojanClientServiceServer

	auth          statistic.Authenticator
	ctx           context.Context
	uploadSpeed   uint64
	downloadSpeed uint64
	lastSent      uint64
	lastRecv      uint64
}

func (s *ClientAPI) GetTraffic(ctx context.Context, req *GetTrafficRequest) (*GetTrafficResponse, error) {
	log.Debug("API: GetTraffic")
	if req.User == nil {
		return nil, common.NewError("User is unspecified")
	}
	if req.User.Hash == "" {
		req.User.Hash = common.SHA224String(req.User.Password)
	}
	valid, user := s.auth.AuthUser(req.User.Hash)
	if !valid {
		return nil, common.NewError("User " + req.User.Hash + " not found")
	}
	sent, recv := user.GetTraffic()
	sentSpeed, recvSpeed := user.GetSpeed()
	resp := &GetTrafficResponse{
		Success: true,
		TrafficTotal: &Traffic{
			UploadTraffic:   sent,
			DownloadTraffic: recv,
		},
		SpeedCurrent: &Speed{
			UploadSpeed:   sentSpeed,
			DownloadSpeed: recvSpeed,
		},
	}
	return resp, nil
}

func RunClientAPI(ctx context.Context, auth statistic.Authenticator) error {
	cfg := config.FromContext(ctx, Name).(*Config)
	if !cfg.API.Enabled {
		return nil
	}
	server, err := newAPIServer(cfg)
	if err != nil {
		return err
	}
	defer server.Stop()
	service := &ClientAPI{
		ctx:  ctx,
		auth: auth,
	}
	RegisterTrojanClientServiceServer(server, service)
	addr, err := net.ResolveIPAddr("ip", cfg.API.APIHost)
	if err != nil {
		return common.NewError("api found invalid addr").Base(err)
	}
	listener, err := net.Listen("tcp", (&net.TCPAddr{
		IP:   addr.IP,
		Port: cfg.API.APIPort,
		Zone: addr.Zone,
	}).String())
	if err != nil {
		return common.NewError("client api failed to listen").Base(err)
	}
	defer listener.Close()
	log.Info("client-side api service is listening on", listener.Addr().String())
	errChan := make(chan error, 1)
	go func() {
		errChan <- server.Serve(listener)
	}()
	select {
	case err := <-errChan:
		return err
	case <-ctx.Done():
		log.Debug("closed")
		return nil
	}
}

func init() {
	api.RegisterHandler(trojan.Name+"_CLIENT", RunClientAPI)
}


================================================
FILE: api/service/client_test.go
================================================
package service

import (
	"context"
	"fmt"
	"testing"
	"time"

	"google.golang.org/grpc"

	"github.com/p4gefau1t/trojan-go/common"
	"github.com/p4gefau1t/trojan-go/config"
	"github.com/p4gefau1t/trojan-go/statistic/memory"
)

func TestClientAPI(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	ctx = config.WithConfig(ctx, memory.Name,
		&memory.Config{
			Passwords: []string{"useless"},
		})
	port := common.PickPort("tcp", "127.0.0.1")
	ctx = config.WithConfig(ctx, Name, &Config{
		APIConfig{
			Enabled: true,
			APIHost: "127.0.0.1",
			APIPort: port,
		},
	})
	auth, err := memory.NewAuthenticator(ctx)
	common.Must(err)
	go RunClientAPI(ctx, auth)

	time.Sleep(time.Second * 3)
	common.Must(auth.AddUser("hash1234"))
	valid, user := auth.AuthUser("hash1234")
	if !valid {
		t.Fail()
	}
	user.AddTraffic(1234, 5678)
	time.Sleep(time.Second)
	conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", port), grpc.WithInsecure())
	common.Must(err)
	client := NewTrojanClientServiceClient(conn)
	resp, err := client.GetTraffic(ctx, &GetTrafficRequest{User: &User{
		Hash: "hash1234",
	}})
	common.Must(err)
	if resp.TrafficTotal.DownloadTraffic != 5678 || resp.TrafficTotal.UploadTraffic != 1234 {
		t.Fail()
	}
	_, err = client.GetTraffic(ctx, &GetTrafficRequest{})
	if err == nil {
		t.Fail()
	}
	cancel()
}


================================================
FILE: api/service/config.go
================================================
package service

import "github.com/p4gefau1t/trojan-go/config"

const Name = "API_SERVICE"

type SSLConfig struct {
	Enabled        bool     `json:"enabled" yaml:"enabled"`
	CertPath       string   `json:"cert" yaml:"cert"`
	KeyPath        string   `json:"key" yaml:"key"`
	VerifyClient   bool     `json:"verify_client" yaml:"verify-client"`
	ClientCertPath []string `json:"client_cert" yaml:"client-cert"`
}

type APIConfig struct {
	Enabled bool      `json:"enabled" yaml:"enabled"`
	APIHost string    `json:"api_addr" yaml:"api-addr"`
	APIPort int       `json:"api_port" yaml:"api-port"`
	SSL     SSLConfig `json:"ssl" yaml:"ssl"`
}

type Config struct {
	API APIConfig `json:"api" yaml:"api"`
}

func init() {
	config.RegisterConfigCreator(Name, func() interface{} {
		return new(Config)
	})
}


================================================
FILE: api/service/gen.sh
================================================
#!/usr/bin/env bash

echo "Processing..."

GOPATH=${GOPATH:-$(go env GOPATH)}
GOBIN=${GOBIN:-$(go env GOBIN)}

if [[ $GOBIN == "" ]]; then
  GOBIN=${GOPATH}/bin
fi

go install -v google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install -v google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

echo "Use protoc-gen-go and protoc-gen-go-grpc in $GOBIN."

protoc --go_out=. \
--go_opt=paths=source_relative \
--go-grpc_out=. \
--go-grpc_opt=paths=source_relative \
--plugin=protoc-gen-go=${GOBIN}/protoc-gen-go \
--plugin=protoc-gen-go-grpc=${GOBIN}/protoc-gen-go-grpc \
api.proto

if [ $? -eq 0 ]; then
  echo "Generated successfully."
fi


================================================
FILE: api/service/server.go
================================================
package service

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"io"
	"io/ioutil"
	"net"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"github.com/p4gefau1t/trojan-go/api"
	"github.com/p4gefau1t/trojan-go/common"
	"github.com/p4gefau1t/trojan-go/config"
	"github.com/p4gefau1t/trojan-go/log"
	"github.com/p4gefau1t/trojan-go/statistic"
	"github.com/p4gefau1t/trojan-go/tunnel/trojan"
)

type ServerAPI struct {
	TrojanServerServiceServer
	auth statistic.Authenticator
}

func (s *ServerAPI) GetUsers(stream TrojanServerService_GetUsersServer) error {
	log.Debug("API: GetUsers")
	for {
		req, err := stream.Recv()
		if err == io.EOF {
			return nil
		}
		if err != nil {
			return err
		}
		if req.User == nil {
			return common.NewError("user is unspecified")
		}
		if req.User.Hash == "" {
			req.User.Hash = common.SHA224String(req.User.Password)
		}
		valid, user := s.auth.AuthUser(req.User.Hash)
		if !valid {
			stream.Send(&GetUsersResponse{
				Success: false,
				Info:    "invalid user: " + req.User.Hash,
			})
			continue
		}
		downloadTraffic, uploadTraffic := user.GetTraffic()
		downloadSpeed, uploadSpeed := user.GetSpeed()
		downloadSpeedLimit, uploadSpeedLimit := user.GetSpeedLimit()
		ipLimit := user.GetIPLimit()
		ipCurrent := user.GetIP()
		err = stream.Send(&GetUsersResponse{
			Success: true,
			Status: &UserStatus{
				User: req.User,
				TrafficTotal: &Traffic{
					UploadTraffic:   uploadTraffic,
					DownloadTraffic: downloadTraffic,
				},
				SpeedCurrent: &Speed{
					DownloadSpeed: downloadSpeed,
					UploadSpeed:   uploadSpeed,
				},
				SpeedLimit: &Speed{
					DownloadSpeed: uint64(downloadSpeedLimit),
					UploadSpeed:   uint64(uploadSpeedLimit),
				},
				IpCurrent: int32(ipCurrent),
				IpLimit:   int32(ipLimit),
			},
		})
		if err != nil {
			return err
		}
	}
}

func (s *ServerAPI) SetUsers(stream TrojanServerService_SetUsersServer) error {
	log.Debug("API: SetUsers")
	for {
		req, err := stream.Recv()
		if err == io.EOF {
			return nil
		}
		if err != nil {
			return err
		}
		if req.Status == nil {
			return common.NewError("status is unspecified")
		}
		if req.Status.User.Hash == "" {
			req.Status.User.Hash = common.SHA224String(req.Status.User.Password)
		}
		switch req.Operation {
		case SetUsersRequest_Add:
			if err = s.auth.AddUser(req.Status.User.Hash); err != nil {
				err = common.NewError("failed to add new user").Base(err)
				break
			}
			if req.Status.SpeedLimit != nil {
				valid, user := s.auth.AuthUser(req.Status.User.Hash)
				if !valid {
					err = common.NewError("failed to auth new user").Base(err)
					continue
				}
				if req.Status.SpeedLimit != nil {
					user.SetSpeedLimit(int(req.Status.SpeedLimit.DownloadSpeed), int(req.Status.SpeedLimit.UploadSpeed))
				}
				if req.Status.TrafficTotal != nil {
					user.SetTraffic(req.Status.TrafficTotal.DownloadTraffic, req.Status.TrafficTotal.UploadTraffic)
				}
				user.SetIPLimit(int(req.Status.IpLimit))
			}
		case SetUsersRequest_Delete:
			err = s.auth.DelUser(req.Status.User.Hash)
		case SetUsersRequest_Modify:
			valid, user := s.auth.AuthUser(req.Status.User.Hash)
			if !valid {
				err = common.NewError("invalid user " + req.Status.User.Hash)
			} else {
				if req.Status.SpeedLimit != nil {
					user.SetSpeedLimit(int(req.Status.SpeedLimit.DownloadSpeed), int(req.Status.SpeedLimit.UploadSpeed))
				}
				if req.Status.TrafficTotal != nil {
					user.SetTraffic(req.Status.TrafficTotal.DownloadTraffic, req.Status.TrafficTotal.UploadTraffic)
				}
				user.SetIPLimit(int(req.Status.IpLimit))
			}
		}
		if err != nil {
			stream.Send(&SetUsersResponse{
				Success: false,
				Info:    err.Error(),
			})
			continue
		}
		stream.Send(&SetUsersResponse{
			Success: true,
		})
	}
}

func (s *ServerAPI) ListUsers(req *ListUsersRequest, stream TrojanServerService_ListUsersServer) error {
	log.Debug("API: ListUsers")
	users := s.auth.ListUsers()
	for _, user := range users {
		downloadTraffic, uploadTraffic := user.GetTraffic()
		downloadSpeed, uploadSpeed := user.GetSpeed()
		downloadSpeedLimit, uploadSpeedLimit := user.GetSpeedLimit()
		ipLimit := user.GetIPLimit()
		ipCurrent := user.GetIP()
		err := stream.Send(&ListUsersResponse{
			Status: &UserStatus{
				User: &User{
					Hash: user.Hash(),
				},
				TrafficTotal: &Traffic{
					DownloadTraffic: downloadTraffic,
					UploadTraffic:   uploadTraffic,
				},
				SpeedCurrent: &Speed{
					DownloadSpeed: downloadSpeed,
					UploadSpeed:   uploadSpeed,
				},
				SpeedLimit: &Speed{
					DownloadSpeed: uint64(downloadSpeedLimit),
					UploadSpeed:   uint64(uploadSpeedLimit),
				},
				IpLimit:   int32(ipLimit),
				IpCurrent: int32(ipCurrent),
			},
		})
		if err != nil {
			return err
		}
	}
	return nil
}

func newAPIServer(cfg *Config) (*grpc.Server, error) {
	var server *grpc.Server
	if cfg.API.SSL.Enabled {
		log.Info("api tls enabled")
		keyPair, err := tls.LoadX509KeyPair(cfg.API.SSL.CertPath, cfg.API.SSL.KeyPath)
		if err != nil {
			return nil, common.NewError("failed to load key pair").Base(err)
		}
		tlsConfig := &tls.Config{
			Certificates: []tls.Certificate{keyPair},
		}
		if cfg.API.SSL.VerifyClient {
			tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
			tlsConfig.ClientCAs = x509.NewCertPool()
			for _, path := range cfg.API.SSL.ClientCertPath {
				log.Debug("loading client cert: " + path)
				certBytes, err := ioutil.ReadFile(path)
				if err != nil {
					return nil, common.NewError("failed to load cert file").Base(err)
				}
				ok := tlsConfig.ClientCAs.AppendCertsFromPEM(certBytes)
				if !ok {
					return nil, common.NewError("invalid client cert")
				}
			}
		}
		creds := credentials.NewTLS(tlsConfig)
		server = grpc.NewServer(grpc.Creds(creds))
	} else {
		server = grpc.NewServer()
	}
	return server, nil
}

func RunServerAPI(ctx context.Context, auth statistic.Authenticator) error {
	cfg := config.FromContext(ctx, Name).(*Config)
	if !cfg.API.Enabled {
		return nil
	}
	service := &ServerAPI{
		auth: auth,
	}
	server, err := newAPIServer(cfg)
	if err != nil {
		return err
	}
	defer server.Stop()
	RegisterTrojanServerServiceServer(server, service)
	addr, err := net.ResolveIPAddr("ip", cfg.API.APIHost)
	if err != nil {
		return common.NewError("api found invalid addr").Base(err)
	}
	listener, err := net.Listen("tcp", (&net.TCPAddr{
		IP:   addr.IP,
		Port: cfg.API.APIPort,
		Zone: addr.Zone,
	}).String())
	if err != nil {
		return common.NewError("server api failed to listen").Base(err)
	}
	defer listener.Close()
	log.Info("server-side api service is listening on", listener.Addr().String())
	errChan := make(chan error, 1)
	go func() {
		errChan <- server.Serve(listener)
	}()
	select {
	case err := <-errChan:
		return err
	case <-ctx.Done():
		log.Debug("closed")
		return nil
	}
}

func init() {
	api.RegisterHandler(trojan.Name+"_SERVER", RunServerAPI)
}


================================================
FILE: api/service/server_test.go
================================================
package service

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"os"
	"testing"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"github.com/p4gefau1t/trojan-go/common"
	"github.com/p4gefau1t/trojan-go/config"
	"github.com/p4gefau1t/trojan-go/statistic/memory"
)

func TestServerAPI(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	ctx = config.WithConfig(ctx, memory.Name,
		&memory.Config{
			Passwords: []string{},
		})
	port := common.PickPort("tcp", "127.0.0.1")
	ctx = config.WithConfig(ctx, Name, &Config{
		APIConfig{
			Enabled: true,
			APIHost: "127.0.0.1",
			APIPort: port,
		},
	})
	auth, err := memory.NewAuthenticator(ctx)
	common.Must(err)
	go RunServerAPI(ctx, auth)
	time.Sleep(time.Second * 3)
	common.Must(auth.AddUser("hash1234"))
	_, user := auth.AuthUser("hash1234")
	conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", port), grpc.WithInsecure())
	common.Must(err)
	server := NewTrojanServerServiceClient(conn)
	stream1, err := server.ListUsers(ctx, &ListUsersRequest{})
	common.Must(err)
	for {
		resp, err := stream1.Recv()
		if err != nil {
			break
		}
		fmt.Println(resp.Status.User.Hash)
		if resp.Status.User.Hash != "hash1234" {
			t.Fail()
		}
		fmt.Println(resp.Status.SpeedCurrent)
		fmt.Println(resp.Status.SpeedLimit)
	}
	stream1.CloseSend()
	user.AddTraffic(1234, 5678)
	time.Sleep(time.Second * 1)
	stream2, err := server.GetUsers(ctx)
	common.Must(err)
	stream2.Send(&GetUsersRequest{
		User: &User{
			Hash: "hash1234",
		},
	})
	resp2, err := stream2.Recv()
	common.Must(err)
	if resp2.Status.TrafficTotal.DownloadTraffic != 1234 || resp2.Status.TrafficTotal.UploadTraffic != 5678 {
		t.Fatal("wrong traffic")
	}

	stream3, err := server.SetUsers(ctx)
	common.Must(err)
	stream3.Send(&SetUsersRequest{
		Status: &UserStatus{
			User: &User{
				Hash: "hash1234",
			},
		},
		Operation: SetUsersRequest_Delete,
	})
	resp3, err := stream3.Recv()
	if err != nil || !resp3.Success {
		t.Fatal("user not exists")
	}
	valid, _ := auth.AuthUser("hash1234")
	if valid {
		t.Fatal("failed to auth")
	}
	stream3.Send(&SetUsersRequest{
		Status: &UserStatus{
			User: &User{
				Hash: "newhash",
			},
		},
		Operation: SetUsersRequest_Add,
	})
	resp3, err = stream3.Recv()
	if err != nil || !resp3.Success {
		t.Fatal("failed to read")
	}
	valid, user = auth.AuthUser("newhash")
	if !valid {
		t.Fatal("failed to auth 2")
	}
	stream3.Send(&SetUsersRequest{
		Status: &UserStatus{
			User: &User{
				Hash: "newhash",
			},
			SpeedLimit: &Speed{
				DownloadSpeed: 5000,
				UploadSpeed:   3000,
			},
			TrafficTotal: &Traffic{
				DownloadTraffic: 1,
				UploadTraffic:   1,
			},
		},
		Operation: SetUsersRequest_Modify,
	})
	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			default:
			}
			user.AddTraffic(200, 0)
		}
	}()
	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			default:
			}
			user.AddTraffic(0, 300)
		}
	}()
	time.Sleep(time.Second * 3)
	for i := 0; i < 3; i++ {
		stream2.Send(&GetUsersRequest{
			User: &User{
				Hash: "newhash",
			},
		})
		resp2, err = stream2.Recv()
		common.Must(err)
		fmt.Println(resp2.Status.SpeedCurrent)
		fmt.Println(resp2.Status.SpeedLimit)
		time.Sleep(time.Second)
	}
	stream2.CloseSend()
	cancel()
}

func TestTLSRSA(t *testing.T) {
	port := common.PickPort("tcp", "127.0.0.1")
	cfg := &Config{
		API: APIConfig{
			Enabled: true,
			APIHost: "127.0.0.1",
			APIPort: port,
			SSL: SSLConfig{
				Enabled:        true,
				CertPath:       "server-rsa2048.crt",
				KeyPath:        "server-rsa2048.key",
				VerifyClient:   false,
				ClientCertPath: []string{"client-rsa2048.crt"},
			},
		},
	}

	ctx := config.WithConfig(context.Background(), Name, cfg)
	ctx = config.WithConfig(ctx, memory.Name,
		&memory.Config{
			Passwords: []string{},
		})

	auth, err := memory.NewAuthenticator(ctx)
	common.Must(err)
	go func() {
		common.Must(RunServerAPI(ctx, auth))
	}()
	time.Sleep(time.Second)
	pool := x509.NewCertPool()
	certBytes, err := os.ReadFile("server-rsa2048.crt")
	common.Must(err)
	pool.AppendCertsFromPEM(certBytes)

	certificate, err := tls.LoadX509KeyPair("client-rsa2048.crt", "client-rsa2048.key")
	common.Must(err)
	creds := credentials.NewTLS(&tls.Config{
		ServerName:   "localhost",
		RootCAs:      pool,
		Certificates: []tls.Certificate{certificate},
	})
	conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", port), grpc.WithTransportCredentials(creds))
	common.Must(err)
	server := NewTrojanServerServiceClient(conn)
	stream, err := server.ListUsers(ctx, &ListUsersRequest{})
	common.Must(err)
	stream.CloseSend()
	conn.Close()
}

func TestTLSECC(t *testing.T) {
	port := common.PickPort("tcp", "127.0.0.1")
	cfg := &Config{
		API: APIConfig{
			Enabled: true,
			APIHost: "127.0.0.1",
			APIPort: port,
			SSL: SSLConfig{
				Enabled:        true,
				CertPath:       "server-ecc.crt",
				KeyPath:        "server-ecc.key",
				VerifyClient:   false,
				ClientCertPath: []string{"client-ecc.crt"},
			},
		},
	}

	ctx := config.WithConfig(context.Background(), Name, cfg)
	ctx = config.WithConfig(ctx, memory.Name,
		&memory.Config{
			Passwords: []string{},
		})

	auth, err := memory.NewAuthenticator(ctx)
	common.Must(err)
	go func() {
		common.Must(RunServerAPI(ctx, auth))
	}()
	time.Sleep(time.Second)
	pool := x509.NewCertPool()
	certBytes, err := os.ReadFile("server-ecc.crt")
	common.Must(err)
	pool.AppendCertsFromPEM(certBytes)

	certificate, err := tls.LoadX509KeyPair("client-ecc.crt", "client-ecc.key")
	common.Must(err)
	creds := credentials.NewTLS(&tls.Config{
		ServerName:   "localhost",
		RootCAs:      pool,
		Certificates: []tls.Certificate{certificate},
	})
	conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", port), grpc.WithTransportCredentials(creds))
	common.Must(err)
	server := NewTrojanServerServiceClient(conn)
	stream, err := server.ListUsers(ctx, &ListUsersRequest{})
	common.Must(err)
	stream.CloseSend()
	conn.Close()
}

var serverRSA2048Cert = `
-----BEGIN CERTIFICATE-----
MIIC5TCCAc2gAwIBAgIJAJqNVe6g/10vMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0yMTA5MTQwNjE1MTFaFw0yNjA5MTMwNjE1MTFaMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAK7bupJ8tmHM3shQ/7N730jzpRsXdNiBxq/Jxx8j+vB3AcxuP5bjXQZqS6YR
5W5vrfLlegtq1E/mmaI3Ht0RfIlzev04Dua9PWmIQJD801nEPknbfgCLXDh+pYr2
sfg8mUh3LjGtrxyH+nmbTjWg7iWSKohmZ8nUDcX94Llo5FxibMAz8OsAwOmUueCH
jP3XswZYHEy+OOP3K0ZEiJy0f5T6ZXk9OWYuPN4VQKJx1qrc9KzZtSPHwqVdkGUi
ase9tOPA4aMutzt0btgW7h7UrvG6C1c/Rr1BxdiYq1EQ+yypnAlyToVQSNbo67zz
wGQk4GeruIkOgJOLdooN/HjhbHMCAwEAAaM6MDgwFAYDVR0RBA0wC4IJbG9jYWxo
b3N0MAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0B
AQsFAAOCAQEASsBzHHYiWDDiBVWUEwVZAduTrslTLNOxG0QHBKsHWIlz/3QlhQil
ywb3OhfMTUR1dMGY5Iq5432QiCHO4IMCOv7tDIkgb4Bc3v/3CRlBlnurtAmUfNJ6
pTRSlK4AjWpGHAEEd/8aCaOE86hMP8WDht8MkJTRrQqpJ1HeDISoKt9nepHOIsj+
I2zLZZtw0pg7FuR4MzWuqOt071iRS46Pupryb3ZEGIWNz5iLrDQod5Iz2ZGSRGqE
rB8idX0mlj5AHRRanVR3PAes+eApsW9JvYG/ImuCOs+ZsukY614zQZdR+SyFm85G
4NICyeQsmiypNHHgw+xZmGqZg65bXNGoyg==
-----END CERTIFICATE-----
`

var serverRSA2048Key = `
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCu27qSfLZhzN7I
UP+ze99I86UbF3TYgcavyccfI/rwdwHMbj+W410GakumEeVub63y5XoLatRP5pmi
Nx7dEXyJc3r9OA7mvT1piECQ/NNZxD5J234Ai1w4fqWK9rH4PJlIdy4xra8ch/p5
m041oO4lkiqIZmfJ1A3F/eC5aORcYmzAM/DrAMDplLngh4z917MGWBxMvjjj9ytG
RIictH+U+mV5PTlmLjzeFUCicdaq3PSs2bUjx8KlXZBlImrHvbTjwOGjLrc7dG7Y
Fu4e1K7xugtXP0a9QcXYmKtREPssqZwJck6FUEjW6Ou888BkJOBnq7iJDoCTi3aK
Dfx44WxzAgMBAAECggEBAKYhib/H0ZhWB4yWuHqUxG4RXtrAjHlvw5Acy5zgmHiC
+Sh7ztrTJf0EXN9pvWwRm1ldgXj7hMBtPaaLbD1pccM9/qo66p17Sq/LjlyyeTOe
affOHIbz4Sij2zCOdkR9fr0EztTQScF3yBhl4Aa/4cO8fcCeWxm86WEldq9x4xWJ
s5WMR4CnrOJhDINLNPQPKX92KyxEQ/RfuBWovx3M0nl3fcUWfESY134t5g/UBFId
In19tZ+pGIpCkxP0U1AZWrlZRA8Q/3sO2orUpoAOdCrGk/DcCTMh0c1pMzbYZ1/i
cYXn38MpUo8QeG4FElUhAv6kzeBIl2tRBMVzIigo+AECgYEA3No1rHdFu6Ox9vC8
E93PTZevYVcL5J5yx6x7khCaOLKKuRXpjOX/h3Ll+hlN2DVAg5Jli/JVGCco4GeK
kbFLSyxG1+E63JbgsVpaEOgvFT3bHHSPSRJDnIU+WkcNQ2u4Ky5ahZzbNdV+4fj2
NO2iMgkm7hoJANrm3IqqW8epenMCgYEAyq+qdNj5DiDzBcDvLwY+4/QmMOOgDqeh
/TzhbDRyr+m4xNT7LLS4s/3wcbkQC33zhMUI3YvOHnYq5Ze/iL/TSloj0QCp1I7L
J7sZeM1XimMBQIpCfOC7lf4tU76Fz0DTHAL+CmX1DgmRJdYO09843VsKkscC968R
4cwL5oGxxgECgYAM4TTsH/CTJtLEIfn19qOWVNhHhvoMlSkAeBCkzg8Qa2knrh12
uBsU3SCIW11s1H40rh758GICDJaXr7InGP3ZHnXrNRlnr+zeqvRBtCi6xma23B1X
F5eV0zd1sFsXqXqOGh/xVtp54z+JEinZoForLNl2XVJVGG8KQZP50kUR/QKBgH4O
8zzpFT0sUPlrHVdp0wODfZ06dPmoWJ9flfPuSsYN3tTMgcs0Owv3C+wu5UPAegxB
X1oq8W8Qn21cC8vJQmgj19LNTtLcXI3BV/5B+Aghu02gr+lq/EA1bYuAG0jjUGlD
kyx0bQzl9lhJ4b70PjGtxc2z6KyTPdPpTB143FABAoGAQDoIUdc77/IWcjzcaXeJ
8abak5rAZA7cu2g2NVfs+Km+njsB0pbTwMnV1zGoFABdaHLdqbthLWtX7WOb1PDD
MQ+kbiLw5uj8IY2HEqJhDGGEdXBqxbW7kyuIAN9Mw+mwKzkikNcFQdxgchWH1d1o
lVkr92iEX+IhIeYb4DN1vQw=
-----END PRIVATE KEY-----
`

var clientRSA2048Cert = `
-----BEGIN CERTIFICATE-----
MIIC5TCCAc2gAwIBAgIJAKD1wSl+Mnk7MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0yMTA5MTQwNjE2MDBaFw0yNjA5MTMwNjE2MDBaMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAJeDu630louuf2V4sw396cGiAnxmTseVRMG+m3PnZ831puAsApm3IWSEcOqI
UMk6s1pgSLysg6GxRZhX4L/ljErjMO4+y8riZjqqR0wd0GnhuNxuXaSUsEmKdDZb
cICqXkZeZRn4jw/7L0xgdAdM2w3LXR6aq6CwveFY3/JncEZFQHH5mnorZdpbheR6
rhvIL6AAI0YEY9uzuQBSrzOml3f7D+x5Xll14HoMN0kCysWt8jSP/An5yP8pL5RO
pn5kNBc8Bx8lykuV1uS8ogncSM7JzmpP1SeAViOq8CqXlJtUbUqVPckMmdfMMtbI
qIO7R5/8imrdhLMi25fAOnfmDzcCAwEAAaM6MDgwFAYDVR0RBA0wC4IJbG9jYWxo
b3N0MAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0B
AQsFAAOCAQEAFu2QPE3x1Sm3SfnHzAhvdjviYkbWvM8rQziIlIevbvA9Nl+vxDBf
N5aRR6Hpxq02J2G/w7tzrKB9IluWdMU1+tilph5bCnwx3QUh/GR4oTsFiTvTZ5br
SNf3xfTyIsL+Hf6iLvEgSt15ziY/334wu9NmQrU0FNZ+Lcc7Mx0OgvuP9Zim+6oo
/FW80R3pUSzUZcUQgsI4Sz7/6nJTxhsc+kqtnOXIQLPC9GA06kP8eN6XjTsavP7f
eZq/yozddOk0dqx8uwmKUOb1Rg+pS8VIhQBRv3UPb4L/07AWSTZSMZLf1+CMgzMY
Jtsxa1MLqPkB7fiAR6SFUFW7Q36gDp/Mdw==
-----END CERTIFICATE-----
`

var clientRSA2048Key = `
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXg7ut9JaLrn9l
eLMN/enBogJ8Zk7HlUTBvptz52fN9abgLAKZtyFkhHDqiFDJOrNaYEi8rIOhsUWY
V+C/5YxK4zDuPsvK4mY6qkdMHdBp4bjcbl2klLBJinQ2W3CAql5GXmUZ+I8P+y9M
YHQHTNsNy10emqugsL3hWN/yZ3BGRUBx+Zp6K2XaW4Xkeq4byC+gACNGBGPbs7kA
Uq8zppd3+w/seV5ZdeB6DDdJAsrFrfI0j/wJ+cj/KS+UTqZ+ZDQXPAcfJcpLldbk
vKIJ3EjOyc5qT9UngFYjqvAql5SbVG1KlT3JDJnXzDLWyKiDu0ef/Ipq3YSzItuX
wDp35g83AgMBAAECggEASOv4CjMrubKUUgwTcWqBdNY6iBDdXaVz4COSweffx/qx
BDdqUP0Yrz4m8loFN7Ru2dJ5b4VAHTQqoLW6z+D08p4B0MicYNsyBI4rnnDC/BLN
XBoqK6n8Zoiigf7kWKimkwufcS51/GUSUJojfdf5ndwAx1f9vmsSGEEkF5C9MrQo
Sa4eyySuXuDS9swrXuTn9FcVcbUoIblgL8GIlmX4S1Xl9NaVS/VAVt42FgpNSYy7
2Qf9Medg3ApimjwkLiDSh0RElirlUwzSg9dx2U+hHwWQWimb2AhA85uK3bpFB/x9
b2agS1uxTar4mk4LFppQqVUuXlpj2hW7HxTdcxGHYQKBgQDGA+Wv3b8FMeszYqqx
BbI5+FeXmQ5AoYNdHyPfCH8f2LX1FTnnQbUvFMJ3UQZl/GGHocgyFNvfDo6YyYsg
2XgcNO/JWMbKEw0HkfMgkaIa3Jfq/PTB386NhqBq5FHiBUrvSHzhaIzpuaokBrRk
jFlcqONK+uj77iRgci4wR59POQKBgQDD4fDZzOGQCy/AWrMX/adk8ymoxhWQj6PN
zhy2GZo9jGwyskQr5neIDQtxRbgokMspxpyPdQG2SxbG/zygyFEaCsp/Xb3iB3aA
2dcktkV9agx+g1WrflTpGG9quW5vQJgQv9FtlVXJdiihN9CQvXAcYRjUA/zkadIL
GsrVF9rh7wKBgFoMLaiDU7neEJKGnQ7xgzI/kD29ebDEgkOXxK1JZN4ro9t3MqTK
ycVGUIUIELvSQNv4I107BR3ztb8fcCiZHLjfDehnecctULCPm5vE/o3uoRtYu0lr
KLhNb6gMenwpYgFc2oV7ERG8v/WwItrSxFSR7QMNBWSD0IEXi4+jEnxpAoGAMJTS
5VG5B76egzh7fpG8eH8Ob/tg0c+uMpbR7CABbw5qr1AjNDgeoTGLCvbdq8HtgVju
7213lTyeU5Bt+vpzkt/mRRx8wZhUPbTJdSN3rJkmrCHql3Pnn0AeMfv3dcQxcsYA
LQuCkUqq3QE4yw0QxxkVzU+H4yaTn4lvkNYvxSUCgYEAgD85MM3DaiNcrevQv6Wb
vSo7jBhd8Q/NawH53V32eIlF9Kkm2mqTmPIQ2OxOtdZ3xm+JmPd20jEVg7NcPL58
t6BoSH10pLaI0CP2NdcYfJTIiHAhuQe7PxPCA0nlHTXgSv97QR7y4qMhbf2j1umc
hKym0tdk2QjR3qqglaD572A=
-----END PRIVATE KEY-----
`

var serverECCCert = `
-----BEGIN CERTIFICATE-----
MIICTDCCAfKgAwIBAgIQDtCrO8cNST2eY2tA/AGrsDAKBggqhkjOPQQDAjBeMQsw
CQYDVQQGEwJDTjEOMAwGA1UEChMFTXlTU0wxKzApBgNVBAsTIk15U1NMIFRlc3Qg
RUNDIC0gRm9yIHRlc3QgdXNlIG9ubHkxEjAQBgNVBAMTCU15U1NMLmNvbTAeFw0y
MTA5MTQwNjQ1MzNaFw0yNjA5MTMwNjQ1MzNaMCExCzAJBgNVBAYTAkNOMRIwEAYD
VQQDEwlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASvYy/r7XR1
Y39lC2JpRJh582zR2CTNynbuolK9a1jsbXaZv+hpBlHkgzMHsWu7LY9Pnb/Dbp4i
1lRASOddD/rLo4HOMIHLMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEF
BQcDAQYIKwYBBQUHAwIwHwYDVR0jBBgwFoAUWxGyVxD0fBhTy3tH4eKznRFXFCYw
YwYIKwYBBQUHAQEEVzBVMCEGCCsGAQUFBzABhhVodHRwOi8vb2NzcC5teXNzbC5j
b20wMAYIKwYBBQUHMAKGJGh0dHA6Ly9jYS5teXNzbC5jb20vbXlzc2x0ZXN0ZWNj
LmNydDAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYIKoZIzj0EAwIDSAAwRQIgDQUa
GEdmKstLMHUmmPMGm/P9S4vvSZV2VHsb3+AEyIUCIQCdJpbyTCz+mEyskhwrGOw/
blh3WBONv6MBtqPpmgE1AQ==
-----END CERTIFICATE-----
`

var serverECCKey = `
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIB8G2suYKuBLoodNIwRMp3JPN1fcZxCt3kcOYIx4nbcPoAoGCCqGSM49
AwEHoUQDQgAEr2Mv6+10dWN/ZQtiaUSYefNs0dgkzcp27qJSvWtY7G12mb/oaQZR
5IMzB7Fruy2PT52/w26eItZUQEjnXQ/6yw==
-----END EC PRIVATE KEY-----
`

var clientECCCert = `
-----BEGIN CERTIFICATE-----
MIICTDCCAfKgAwIBAgIQb5FLuCggTiWFtt/dPh+2bTAKBggqhkjOPQQDAjBeMQsw
CQYDVQQGEwJDTjEOMAwGA1UEChMFTXlTU0wxKzApBgNVBAsTIk15U1NMIFRlc3Qg
RUNDIC0gRm9yIHRlc3QgdXNlIG9ubHkxEjAQBgNVBAMTCU15U1NMLmNvbTAeFw0y
MTA5MTQwNjQ2MTFaFw0yNjA5MTMwNjQ2MTFaMCExCzAJBgNVBAYTAkNOMRIwEAYD
VQQDEwlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQME+DnYtcK
lbcZmc33oEtoeRWH61DYdl4ei/bM+vkv01MkBB+YTZl0yofJIFJYsfU5pMFK+uyw
D4qdcklKPGIKo4HOMIHLMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEF
BQcDAQYIKwYBBQUHAwIwHwYDVR0jBBgwFoAUWxGyVxD0fBhTy3tH4eKznRFXFCYw
YwYIKwYBBQUHAQEEVzBVMCEGCCsGAQUFBzABhhVodHRwOi8vb2NzcC5teXNzbC5j
b20wMAYIKwYBBQUHMAKGJGh0dHA6Ly9jYS5teXNzbC5jb20vbXlzc2x0ZXN0ZWNj
LmNydDAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYIKoZIzj0EAwIDSAAwRQIgfjaU
sCfgklPsjHzs3fUtSRGfWoRLFsRBO66RtHJSzrYCIQDAxgx0FB0mAXbflj4mXJVA
9/SjaCI40D6MhMnJhQS7Zg==
-----END CERTIFICATE-----
`

var clientECCKey = `
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEINgbap6WOGXm8V+ghIyporGcGWnggbjjP9xNsxhn+0sqoAoGCCqGSM49
AwEHoUQDQgAEDBPg52LXCpW3GZnN96BLaHkVh+tQ2HZeHov2zPr5L9NTJAQfmE2Z
dMqHySBSWLH1OaTBSvrssA+KnXJJSjxiCg==
-----END EC PRIVATE KEY-----
`

func init() {
	os.WriteFile("server-rsa2048.crt", []byte(serverRSA2048Cert), 0o777)
	os.WriteFile("server-rsa2048.key", []byte(serverRSA2048Key), 0o777)
	os.WriteFile("client-rsa2048.crt", []byte(clientRSA2048Cert), 0o777)
	os.WriteFile("client-rsa2048.key", []byte(clientRSA2048Key), 0o777)

	os.WriteFile("server-ecc.crt", []byte(serverECCCert), 0o777)
	os.WriteFile("server-ecc.key", []byte(serverECCKey), 0o777)
	os.WriteFile("client-ecc.crt", []byte(clientECCCert), 0o777)
	os.WriteFile("client-ecc.key", []byte(clientECCKey), 0o777)
}


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

import (
	"crypto/sha256"
	"fmt"
	"os"
	"path/filepath"

	"github.com/p4gefau1t/trojan-go/log"
)

type Runnable interface {
	Run() error
	Close() error
}

func SHA224String(password string) string {
	hash := sha256.New224()
	hash.Write([]byte(password))
	val := hash.Sum(nil)
	str := ""
	for _, v := range val {
		str += fmt.Sprintf("%02x", v)
	}
	return str
}

func GetProgramDir() string {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatal(err)
	}
	return dir
}

func GetAssetLocation(file string) string {
	if filepath.IsAbs(file) {
		return file
	}
	if loc := os.Getenv("TROJAN_GO_LOCATION_ASSET"); loc != "" {
		absPath, err := filepath.Abs(loc)
		if err != nil {
			log.Fatal(err)
		}
		log.Debugf("env set: TROJAN_GO_LOCATION_ASSET=%s", absPath)
		return filepath.Join(absPath, file)
	}
	return filepath.Join(GetProgramDir(), file)
}


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

import (
	"fmt"
)

type Error struct {
	info string
}

func (e *Error) Error() string {
	return e.info
}

func (e *Error) Base(err error) *Error {
	if err != nil {
		e.info += " | " + err.Error()
	}
	return e
}

func NewError(info string) *Error {
	return &Error{
		info: info,
	}
}

func Must(err error) {
	if err != nil {
		fmt.Println(err)
		panic(err)
	}
}

func Must2(_ interface{}, err error) {
	if err != nil {
		fmt.Println(err)
		panic(err)
	}
}


================================================
FILE: common/geodata/cache.go
================================================
package geodata

import (
	"io/ioutil"
	"strings"

	v2router "github.com/v2fly/v2ray-core/v4/app/router"
	"google.golang.org/protobuf/proto"

	"github.com/p4gefau1t/trojan-go/common"
	"github.com/p4gefau1t/trojan-go/log"
)

type geoipCache map[string]*v2router.GeoIP

func (g geoipCache) Has(key string) bool {
	return !(g.Get(key) == nil)
}

func (g geoipCache) Get(key string) *v2router.GeoIP {
	if g == nil {
		return nil
	}
	return g[key]
}

func (g geoipCache) Set(key string, value *v2router.GeoIP) {
	if g == nil {
		g = make(map[string]*v2router.GeoIP)
	}
	g[key] = value
}

func (g geoipCache) Unmarshal(filename, code string) (*v2router.GeoIP, error) {
	asset := common.GetAssetLocation(filename)
	idx := strings.ToLower(asset + ":" + code)
	if g.Has(idx) {
		log.Debugf("geoip cache HIT: %s -> %s", code, idx)
		return g.Get(idx), nil
	}

	geoipBytes, err := Decode(asset, code)
	switch err {
	case nil:
		var geoip v2router.GeoIP
		if err := proto.Unmarshal(geoipBytes, &geoip); err != nil {
			return nil, err
		}
		g.Set(idx, &geoip)
		return &geoip, nil

	case ErrCodeNotFound:
		return nil, common.NewError("country code " + code + " not found in " + filename)

	case ErrFailedToReadBytes, ErrFailedToReadExpectedLenBytes,
		ErrInvalidGeodataFile, ErrInvalidGeodataVarintLength:
		log.Warnf("failed to decode geoip file: %s, fallback to the original ReadFile method", filename)
		geoipBytes, err = ioutil.ReadFile(asset)
		if err != nil {
			return nil, err
		}
		var geoipList v2router.GeoIPList
		if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
			return nil, err
		}
		for _, geoip := range geoipList.GetEntry() {
			if strings.EqualFold(code, geoip.GetCountryCode()) {
				g.Set(idx, geoip)
				return geoip, nil
			}
		}

	default:
		return nil, err
	}

	return nil, common.NewError("country code " + code + " not found in " + filename)
}

type geositeCache map[string]*v2router.GeoSite

func (g geositeCache) Has(key string) bool {
	return !(g.Get(key) == nil)
}

func (g geositeCache) Get(key string) *v2router.GeoSite {
	if g == nil {
		return nil
	}
	return g[key]
}

func (g geositeCache) Set(key string, value *v2router.GeoSite) {
	if g == nil {
		g = make(map[string]*v2router.GeoSite)
	}
	g[key] = value
}

func (g geositeCache) Unmarshal(filename, code string) (*v2router.GeoSite, error) {
	asset := common.GetAssetLocation(filename)
	idx := strings.ToLower(asset + ":" + code)
	if g.Has(idx) {
		log.Debugf("geosite cache HIT: %s -> %s", code, idx)
		return g.Get(idx), nil
	}

	geositeBytes, err := Decode(asset, code)
	switch err {
	case nil:
		var geosite v2router.GeoSite
		if err := proto.Unmarshal(geositeBytes, &geosite); err != nil {
			return nil, err
		}
		g.Set(idx, &geosite)
		return &geosite, nil

	case ErrCodeNotFound:
		return nil, common.NewError("list " + code + " not found in " + filename)

	case ErrFailedToReadBytes, ErrFailedToReadExpectedLenBytes,
		ErrInvalidGeodataFile, ErrInvalidGeodataVarintLength:
		log.Warnf("failed to decode geoip file: %s, fallback to the original ReadFile method", filename)
		geositeBytes, err = ioutil.ReadFile(asset)
		if err != nil {
			return nil, err
		}
		var geositeList v2router.GeoSiteList
		if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
			return nil, err
		}
		for _, geosite := range geositeList.GetEntry() {
			if strings.EqualFold(code, geosite.GetCountryCode()) {
				g.Set(idx, geosite)
				return geosite, nil
			}
		}

	default:
		return nil, err
	}

	return nil, common.NewError("list " + code + " not found in " + filename)
}


================================================
FILE: common/geodata/decode.go
================================================
// Package geodata includes utilities to decode and parse the geoip & geosite dat files.
//
// It relies on the proto structure of GeoIP, GeoIPList, GeoSite and GeoSiteList in
// github.com/v2fly/v2ray-core/v4/app/router/config.proto to comply with following rules:
//
// 1. GeoIPList and GeoSiteList cannot be changed
// 2. The country_code in GeoIP and GeoSite must be
//    a length-delimited `string`(wired type) and has field_number set to 1
//
package geodata

import (
	"errors"
	"io"
	"os"
	"strings"

	"google.golang.org/protobuf/encoding/protowire"
)

var (
	ErrFailedToReadBytes            = errors.New("failed to read bytes")
	ErrFailedToReadExpectedLenBytes = errors.New("failed to read expected length of bytes")
	ErrInvalidGeodataFile           = errors.New("invalid geodata file")
	ErrInvalidGeodataVarintLength   = errors.New("invalid geodata varint length")
	ErrCodeNotFound                 = errors.New("code not found")
)

func EmitBytes(f io.ReadSeeker, code string) ([]byte, error) {
	count := 1
	isInner := false
	tempContainer := make([]byte, 0, 5)

	var result []byte
	var advancedN uint64 = 1
	var geoDataVarintLength, codeVarintLength, varintLenByteLen uint64 = 0, 0, 0

Loop:
	for {
		container := make([]byte, advancedN)
		bytesRead, err := f.Read(container)
		if err == io.EOF {
			return nil, ErrCodeNotFound
		}
		if err != nil {
			return nil, ErrFailedToReadBytes
		}
		if bytesRead != len(container) {
			return nil, ErrFailedToReadExpectedLenBytes
		}

		switch count {
		case 1, 3: // data type ((field_number << 3) | wire_type)
			if container[0] != 10 { // byte `0A` equals to `10` in decimal
				return nil, ErrInvalidGeodataFile
			}
			advancedN = 1
			count++
		case 2, 4: // data length
			tempContainer = append(tempContainer, container...)
			if container[0] > 127 { // max one-byte-length byte `7F`(0FFF FFFF) equals to `127` in decimal
				advancedN = 1
				goto Loop
			}
			lenVarint, n := protowire.ConsumeVarint(tempContainer)
			if n < 0 {
				return nil, ErrInvalidGeodataVarintLength
			}
			tempContainer = nil
			if !isInner {
				isInner = true
				geoDataVarintLength = lenVarint
				advancedN = 1
			} else {
				isInner = false
				codeVarintLength = lenVarint
				varintLenByteLen = uint64(n)
				advancedN = codeVarintLength
			}
			count++
		case 5: // data value
			if strings.EqualFold(string(container), code) {
				count++
				offset := -(1 + int64(varintLenByteLen) + int64(codeVarintLength))
				f.Seek(offset, 1)               // back to the start of GeoIP or GeoSite varint
				advancedN = geoDataVarintLength // the number of bytes to be read in next round
			} else {
				count = 1
				offset := int64(geoDataVarintLength) - int64(codeVarintLength) - int64(varintLenByteLen) - 1
				f.Seek(offset, 1) // skip the unmatched GeoIP or GeoSite varint
				advancedN = 1     // the next round will be the start of another GeoIPList or GeoSiteList
			}
		case 6: // matched GeoIP or GeoSite varint
			result = container
			break Loop
		}
	}

	return result, nil
}

func Decode(filename, code string) ([]byte, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	geoBytes, err := EmitBytes(f, code)
	if err != nil {
		return nil, err
	}
	return geoBytes, nil
}


================================================
FILE: common/geodata/decode_test.go
================================================
package geodata_test

import (
	"bytes"
	"errors"
	"io/fs"
	"os"
	"path/filepath"
	"runtime"
	"testing"

	"github.com/p4gefau1t/trojan-go/common"
	"github.com/p4gefau1t/trojan-go/common/geodata"
)

func init() {
	const (
		geoipURL   = "https://raw.githubusercontent.com/v2fly/geoip/release/geoip.dat"
		geositeURL = "https://raw.githubusercontent.com/v2fly/domain-list-community/release/dlc.dat"
	)

	wd, err := os.Getwd()
	common.Must(err)

	tempPath := filepath.Join(wd, "..", "..", "test", "temp")
	os.Setenv("TROJAN_GO_LOCATION_ASSET", tempPath)

	geoipPath := common.GetAssetLocation("geoip.dat")
	geositePath := common.GetAssetLocation("geosite.dat")

	if _, err := os.Stat(geoipPath); err != nil && errors.Is(err, fs.ErrNotExist) {
		common.Must(os.MkdirAll(tempPath, 0o755))
		geoipBytes, err := common.FetchHTTPContent(geoipURL)
		common.Must(err)
		common.Must(common.WriteFile(geoipPath, geoipBytes))
	}
	if _, err := os.Stat(geositePath); err != nil && errors.Is(err, fs.ErrNotExist) {
		common.Must(os.MkdirAll(tempPath, 0o755))
		geositeBytes, err := common.FetchHTTPContent(geositeURL)
		common.Must(err)
		common.Must(common.WriteFile(geositePath, geositeBytes))
	}
}

func TestDecodeGeoIP(t *testing.T) {
	filename := common.GetAssetLocation("geoip.dat")
	result, err := geodata.Decode(filename, "test")
	if err != nil {
		t.Error(err)
	}

	expected := []byte{10, 4, 84, 69, 83, 84, 18, 8, 10, 4, 127, 0, 0, 0, 16, 8}
	if !bytes.Equal(result, expected) {
		t.Errorf("failed to load geoip:test, expected: %v, got: %v", expected, result)
	}
}

func TestDecodeGeoSite(t *testing.T) {
	filename := common.GetAssetLocation("geosite.dat")
	result, err := geodata.Decode(filename, "test")
	if err != nil {
		t.Error(err)
	}

	expected := []byte{10, 4, 84, 69, 83, 84, 18, 20, 8, 3, 18, 16, 116, 101, 115, 116, 46, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109}
	if !bytes.Equal(result, expected) {
		t.Errorf("failed to load geosite:test, expected: %v, got: %v", expected, result)
	}
}

func BenchmarkLoadGeoIP(b *testing.B) {
	m1 := runtime.MemStats{}
	m2 := runtime.MemStats{}

	loader := geodata.NewGeodataLoader()

	runtime.ReadMemStats(&m1)
	cn, _ := loader.LoadGeoIP("cn")
	private, _ := loader.LoadGeoIP("private")
	runtime.KeepAlive(cn)
	runtime.KeepAlive(private)
	runtime.ReadMemStats(&m2)

	b.ReportMetric(float64(m2.Alloc-m1.Alloc)/1024, "KiB(GeoIP-Alloc)")
	b.ReportMetric(float64(m2.TotalAlloc-m1.TotalAlloc)/1024/1024, "MiB(GeoIP-TotalAlloc)")
}

func BenchmarkLoadGeoSite(b *testing.B) {
	m3 := runtime.MemStats{}
	m4 := runtime.MemStats{}

	loader := geodata.NewGeodataLoader()

	runtime.ReadMemStats(&m3)
	cn, _ := loader.LoadGeoSite("cn")
	notcn, _ := loader.LoadGeoSite("geolocation-!cn")
	private, _ := loader.LoadGeoSite("private")
	runtime.KeepAlive(cn)
	runtime.KeepAlive(notcn)
	runtime.KeepAlive(private)
	runtime.ReadMemStats(&m4)

	b.ReportMetric(float64(m4.Alloc-m3.Alloc)/1024/1024, "MiB(GeoSite-Alloc)")
	b.ReportMetric(float64(m4.TotalAlloc-m3.TotalAlloc)/1024/1024, "MiB(GeoSite-TotalAlloc)")
}


================================================
FILE: common/geodata/interface.go
================================================
package geodata

import v2router "github.com/v2fly/v2ray-core/v4/app/router"

type GeodataLoader interface {
	LoadIP(filename, country string) ([]*v2router.CIDR, error)
	LoadSite(filename, list string) ([]*v2router.Domain, error)
	LoadGeoIP(country string) ([]*v2router.CIDR, error)
	LoadGeoSite(list string) ([]*v2router.Domain, error)
}


================================================
FILE: common/geodata/loader.go
================================================
package geodata

import (
	"runtime"

	v2router "github.com/v2fly/v2ray-core/v4/app/router"
)

type geodataCache struct {
	geoipCache
	geositeCache
}

func NewGeodataLoader() GeodataLoader {
	return &geodataCache{
		make(map[string]*v2router.GeoIP),
		make(map[string]*v2router.GeoSite),
	}
}

func (g *geodataCache) LoadIP(filename, country string) ([]*v2router.CIDR, error) {
	geoip, err := g.geoipCache.Unmarshal(filename, country)
	if err != nil {
		return nil, err
	}
	runtime.GC()
	return geoip.Cidr, nil
}

func (g *geodataCache) LoadSite(filename, list string) ([]*v2router.Domain, error) {
	geosite, err := g.geositeCache.Unmarshal(filename, list)
	if err != nil {
		return nil, err
	}
	runtime.GC()
	return geosite.Domain, nil
}

func (g *geodataCache) LoadGeoIP(country string) ([]*v2router.CIDR, error) {
	return g.LoadIP("geoip.dat", country)
}

func (g *geodataCache) LoadGeoSite(list string) ([]*v2router.Domain, error) {
	return g.LoadSite("geosite.dat", list)
}


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

import (
	"io"
	"net"
	"sync"

	"github.com/p4gefau1t/trojan-go/log"
)

type RewindReader struct {
	mu         sync.Mutex
	rawReader  io.Reader
	buf        []byte
	bufReadIdx int
	rewound    bool
	buffering  bool
	bufferSize int
}

func (r *RewindReader) Read(p []byte) (int, error) {
	r.mu.Lock()
	defer r.mu.Unlock()

	if r.rewound {
		if len(r.buf) > r.bufReadIdx {
			n := copy(p, r.buf[r.bufReadIdx:])
			r.bufReadIdx += n
			return n, nil
		}
		r.rewound = false // all buffering content has been read
	}
	n, err := r.rawReader.Read(p)
	if r.buffering {
		r.buf = append(r.buf, p[:n]...)
		if len(r.buf) > r.bufferSize*2 {
			log.Debug("read too many bytes!")
		}
	}
	return n, err
}

func (r *RewindReader) ReadByte() (byte, error) {
	buf := [1]byte{}
	_, err := r.Read(buf[:])
	return buf[0], err
}

func (r *RewindReader) Discard(n int) (int, error) {
	buf := [128]byte{}
	if n < 128 {
		return r.Read(buf[:n])
	}
	for discarded := 0; discarded+128 < n; discarded += 128 {
		_, err := r.Read(buf[:])
		if err != nil {
			return discarded, err
		}
	}
	if rest := n % 128; rest != 0 {
		return r.Read(buf[:rest])
	}
	return n, nil
}

func (r *RewindReader) Rewind() {
	r.mu.Lock()
	if r.bufferSize == 0 {
		panic("no buffer")
	}
	r.rewound = true
	r.bufReadIdx = 0
	r.mu.Unlock()
}

func (r *RewindReader) StopBuffering() {
	r.mu.Lock()
	r.buffering = false
	r.mu.Unlock()
}

func (r *RewindReader) SetBufferSize(size int) {
	r.mu.Lock()
	if size == 0 { // disable buffering
		if !r.buffering {
			panic("reader is disabled")
		}
		r.buffering = false
		r.buf = nil
		r.bufReadIdx = 0
		r.bufferSize = 0
	} else {
		if r.buffering {
			panic("reader is buffering")
		}
		r.buffering = true
		r.bufReadIdx = 0
		r.bufferSize = size
		r.buf = make([]byte, 0, size)
	}
	r.mu.Unlock()
}

type RewindConn struct {
	net.Conn
	*RewindReader
}

func (c *RewindConn) Read(p []byte) (int, error) {
	return c.RewindReader.Read(p)
}

func NewRewindConn(conn net.Conn) *RewindConn {
	return &RewindConn{
		Conn: conn,
		RewindReader: &RewindReader{
			rawReader: conn,
		},
	}
}

type StickyWriter struct {
	rawWriter   io.Writer
	writeBuffer []byte
	MaxBuffered int
}

func (w *StickyWriter) Write(p []byte) (int, error) {
	if w.MaxBuffered > 0 {
		w.MaxBuffered--
		w.writeBuffer = append(w.writeBuffer, p...)
		if w.MaxBuffered != 0 {
			return len(p), nil
		}
		w.MaxBuffered = 0
		_, err := w.rawWriter.Write(w.writeBuffer)
		w.writeBuffer = nil
		return len(p), err
	}
	return w.rawWriter.Write(p)
}


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

import (
	"bytes"
	"crypto/rand"
	"testing"

	"github.com/v2fly/v2ray-core/v4/common"
)

func TestBufferedReader(t *testing.T) {
	payload := [1024]byte{}
	rand.Reader.Read(payload[:])
	rawReader := bytes.NewBuffer(payload[:])
	r := RewindReader{
		rawReader: rawReader,
	}
	r.SetBufferSize(2048)
	buf1 := make([]byte, 512)
	buf2 := make([]byte, 512)
	common.Must2(r.Read(buf1))
	r.Rewind()
	common.Must2(r.Read(buf2))
	if !bytes.Equal(buf1, buf2) {
		t.Fail()
	}
	buf3 := make([]byte, 512)
	common.Must2(r.Read(buf3))
	if !bytes.Equal(buf3, payload[512:]) {
		t.Fail()
	}
	r.Rewind()
	buf4 := make([]byte, 1024)
	common.Must2(r.Read(buf4))
	if !bytes.Equal(payload[:], buf4) {
		t.Fail()
	}
}


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

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
)

const (
	KiB = 1024
	MiB = KiB * 1024
	GiB = MiB * 1024
)

func HumanFriendlyTraffic(bytes uint64) string {
	if bytes <= KiB {
		return fmt.Sprintf("%d B", bytes)
	}
	if bytes <= MiB {
		return fmt.Sprintf("%.2f KiB", float32(bytes)/KiB)
	}
	if bytes <= GiB {
		return fmt.Sprintf("%.2f MiB", float32(bytes)/MiB)
	}
	return fmt.Sprintf("%.2f GiB", float32(bytes)/GiB)
}

func PickPort(network string, host string) int {
	switch network {
	case "tcp":
		for retry := 0; retry < 16; retry++ {
			l, err := net.Listen("tcp", host+":0")
			if err != nil {
				continue
			}
			defer l.Close()
			_, port, err := net.SplitHostPort(l.Addr().String())
			Must(err)
			p, err := strconv.ParseInt(port, 10, 32)
			Must(err)
			return int(p)
		}
	case "udp":
		for retry := 0; retry < 16; retry++ {
			conn, err := net.ListenPacket("udp", host+":0")
			if err != nil {
				continue
			}
			defer conn.Close()
			_, port, err := net.SplitHostPort(conn.LocalAddr().String())
			Must(err)
			p, err := strconv.ParseInt(port, 10, 32)
			Must(err)
			return int(p)
		}
	default:
		return 0
	}
	return 0
}

func WriteAllBytes(writer io.Writer, payload []byte) error {
	for len(payload) > 0 {
		n, err := writer.Write(payload)
		if err != nil {
			return err
		}
		payload = payload[n:]
	}
	return nil
}

func WriteFile(path string, payload []byte) error {
	writer, err := os.Create(path)
	if err != nil {
		return err
	}
	defer writer.Close()

	return WriteAllBytes(writer, payload)
}

func FetchHTTPContent(target string) ([]byte, error) {
	parsedTarget, err := url.Parse(target)
	if err != nil {
		return nil, fmt.Errorf("invalid URL: %s", target)
	}

	if s := strings.ToLower(parsedTarget.Scheme); s != "http" && s != "https" {
		return nil, fmt.Errorf("invalid scheme: %s", parsedTarget.Scheme)
	}

	client := &http.Client{
		Timeout: 30 * time.Second,
	}
	resp, err := client.Do(&http.Request{
		Method: "GET",
		URL:    parsedTarget,
		Close:  true,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to dial to %s", target)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("unexpected HTTP status code: %d", resp.StatusCode)
	}

	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read HTTP response")
	}

	return content, nil
}


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

// Notifier is a utility for notifying changes. The change producer may notify changes multiple time, and the consumer may get notified asynchronously.
type Notifier struct {
	c chan struct{}
}

// NewNotifier creates a new Notifier.
func NewNotifier() *Notifier {
	return &Notifier{
		c: make(chan struct{}, 1),
	}
}

// Signal signals a change, usually by producer. This method never blocks.
func (n *Notifier) Signal() {
	select {
	case n.c <- struct{}{}:
	default:
	}
}

// Wait returns a channel for waiting for changes. The returned channel never gets closed.
func (n *Notifier) Wait() <-chan struct{} {
	return n.c
}


================================================
FILE: component/api.go
================================================
//go:build api || full
// +build api full

package build

import (
	_ "github.com/p4gefau1t/trojan-go/api/control"
	_ "github.com/p4gefau1t/trojan-go/api/service"
)


================================================
FILE: component/base.go
================================================
package build

import (
	_ "github.com/p4gefau1t/trojan-go/log/golog"
	_ "github.com/p4gefau1t/trojan-go/statistic/memory"
	_ "github.com/p4gefau1t/trojan-go/version"
)


================================================
FILE: component/client.go
================================================
//go:build client || full || mini
// +build client full mini

package build

import (
	_ "github.com/p4gefau1t/trojan-go/proxy/client"
)


================================================
FILE: component/custom.go
================================================
//go:build custom || full
// +build custom full

package build

import (
	_ "github.com/p4gefau1t/trojan-go/proxy/custom"
	_ "github.com/p4gefau1t/trojan-go/tunnel/adapter"
	_ "github.com/p4gefau1t/trojan-go/tunnel/dokodemo"
	_ "github.com/p4gefau1t/trojan-go/tunnel/freedom"
	_ "github.com/p4gefau1t/trojan-go/tunnel/http"
	_ "github.com/p4gefau1t/trojan-go/tunnel/mux"
	_ "github.com/p4gefau1t/trojan-go/tunnel/router"
	_ "github.com/p4gefau1t/trojan-go/tunnel/shadowsocks"
	_ "github.com/p4gefau1t/trojan-go/tunnel/simplesocks"
	_ "github.com/p4gefau1t/trojan-go/tunnel/socks"
	_ "github.com/p4gefau1t/trojan-go/tunnel/tls"
	_ "github.com/p4gefau1t/trojan-go/tunnel/tproxy"
	_ "github.com/p4gefau1t/trojan-go/tunnel/transport"
	_ "github.com/p4gefau1t/trojan-go/tunnel/trojan"
	_ "github.com/p4gefau1t/trojan-go/tunnel/websocket"
)


================================================
FILE: component/forward.go
================================================
//go:build forward || full || mini
// +build forward full mini

package build

import (
	_ "github.com/p4gefau1t/trojan-go/proxy/forward"
)


================================================
FILE: component/mysql.go
================================================
//go:build mysql || full || mini
// +build mysql full mini

package build

import (
	_ "github.com/p4gefau1t/trojan-go/statistic/mysql"
)


================================================
FILE: component/nat.go
================================================
//go:build nat || full || mini
// +build nat full mini

package build

import (
	_ "github.com/p4gefau1t/trojan-go/proxy/nat"
)


================================================
FILE: component/other.go
================================================
//go:build other || full
// +build other full

package build

import (
	_ "github.com/p4gefau1t/trojan-go/easy"
	_ "github.com/p4gefau1t/trojan-go/url"
)


================================================
FILE: component/server.go
================================================
//go:build server || full || mini
// +build server full mini

package build

import (
	_ "github.com/p4gefau1t/trojan-go/proxy/server"
)


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

import (
	"context"
	"encoding/json"

	"gopkg.in/yaml.v3"
)

var creators = make(map[string]Creator)

// Creator creates default config struct for a module
type Creator func() interface{}

// RegisterConfigCreator registers a config struct for parsing
func RegisterConfigCreator(name string, creator Creator) {
	name += "_CONFIG"
	creators[name] = creator
}

func parseJSON(data []byte) (map[string]interface{}, error) {
	result := make(map[string]interface{})
	for name, creator := range creators {
		config := creator()
		if err := json.Unmarshal(data, config); err != nil {
			return nil, err
		}
		result[name] = config
	}
	return result, nil
}

func parseYAML(data []byte) (map[string]interface{}, error) {
	result := make(map[string]interface{})
	for name, creator := range creators {
		config := creator()
		if err := yaml.Unmarshal(data, config); err != nil {
			return nil, err
		}
		result[name] = config
	}
	return result, nil
}

func WithJSONConfig(ctx context.Context, data []byte) (context.Context, error) {
	var configs map[string]interface{}
	var err error
	configs, err = parseJSON(data)
	if err != nil {
		return ctx, err
	}
	for name, config := range configs {
		ctx = context.WithValue(ctx, name, config)
	}
	return ctx, nil
}

func WithYAMLConfig(ctx context.Context, data []byte) (context.Context, error) {
	var configs map[string]interface{}
	var err error
	configs, err = parseYAML(data)
	if err != nil {
		return ctx, err
	}
	for name, config := range configs {
		ctx = context.WithValue(ctx, name, config)
	}
	return ctx, nil
}

func WithConfig(ctx context.Context, name string, cfg interface{}) context.Context {
	name += "_CONFIG"
	return context.WithValue(ctx, name, cfg)
}

// FromContext extracts config from a context
func FromContext(ctx context.Context, name string) interface{} {
	return ctx.Value(name + "_CONFIG")
}


================================================
FILE: config/config_test.go
================================================
package config

import (
	"context"
	"testing"

	"github.com/p4gefau1t/trojan-go/common"
)

type Foo struct {
	Field1 string `json,yaml:"field1"`
	Field2 bool   `json:"field2" yaml:"field2"`
}

type TestStruct struct {
	Field1 string `json,yaml:"field1"`
	Field2 bool   `json,yaml:"field2"`
	Field3 []Foo  `json,yaml:"field3"`
}

func creator() interface{} {
	return &TestStruct{}
}

func TestJSONConfig(t *testing.T) {
	RegisterConfigCreator("test", creator)
	data := []byte(`
	{
		"field1": "test1",
		"field2": true,
		"field3": [
			{
				"field1": "aaaa",
				"field2": true
			}
		]
	}
	`)
	ctx, err := WithJSONConfig(context.Background(), data)
	common.Must(err)
	c := FromContext(ctx, "test").(*TestStruct)
	if c.Field1 != "test1" || c.Field2 != true {
		t.Fail()
	}
}

func TestYAMLConfig(t *testing.T) {
	RegisterConfigCreator("test", creator)
	data := []byte(`
field1: 012345678
field2: true
field3:
  - field1: test
    field2: true
`)
	ctx, err := WithYAMLConfig(context.Background(), data)
	common.Must(err)
	c := FromContext(ctx, "test").(*TestStruct)
	if c.Field1 != "012345678" || c.Field2 != true || c.Field3[0].Field1 != "test" {
		t.Fail()
	}
}


================================================
FILE: constant/constant.go
================================================
package constant

var (
	Version = "Custom Version"
	Commit  = "Unknown Git Commit ID"
)


================================================
FILE: docs/.gitignore
================================================
public/
themes/

================================================
FILE: docs/Makefile
================================================
.PHONY: default clean hugo hugo-build

default: hugo

clean:
	rm -rf public/

hugo-build: clean hugo-themes
	hugo --enableGitInfo --source .

hugo:
	hugo server --disableFastRender --enableGitInfo --watch --source .
	# hugo server -D

hugo-themes:
	rm -rf themes
	mkdir themes
	git clone --depth=1 https://github.com/llc1123/hugo-theme-techdoc.git themes/hugo-theme-techdoc
	rm -rf themes/hugo-theme-techdoc/.git


================================================
FILE: docs/archetypes/default.md
================================================
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---



================================================
FILE: docs/config.toml
================================================
baseURL = "https://p4gefau1t.github.io/trojan-go"

languageCode = "zh-CN"
title = "Trojan-Go Docs"
theme = "hugo-theme-techdoc"

hasCJKLanguage = true
metaDataFormat = "yaml"

defaultContentLanguage = "zh"
defaultContentLanguageInSubdir= true
enableMissingTranslationPlaceholders = false

[params]

    # Source Code repository section
    description = "An unidentifiable mechanism that helps you bypass GFW."
    github_repository = "https://github.com/p4gefau1t/trojan-go"

    # Documentation repository section
    # documentation repository (set edit link to documentation repository)
    github_doc_repository = "https://github.com/p4gefau1t/trojan-go/docs"

    # Analytic section
    google_analytics_id = "" # Your Google Analytics tracking id
    tag_manager_container_id = "" # Your Google Tag Manager container id
    google_site_verification = "" # Your Google Site Verification for Search Console

    # Open Graph and Twitter Cards settings section
    # Open Graph settings for each page are set on the front matter.
    # See https://gohugo.io/templates/internal/#open-graph
    # See https://gohugo.io/templates/internal/#twitter-cards
    title = "Trojan-Go Docs"
    images = ["images/og-image.png"] # Open graph images are placed in `static/images`

    # Theme settings section
    # Theme color
    # See color value reference https://developer.mozilla.org/en-US/docs/Web/CSS/color
    custom_font_color = ""
    custom_background_color = ""

    # Documentation Menu section
    # Menu style settings
    menu_style = "open-menu" # "open-menu" or "slide-menu"

    # Date format
    dateformat = "" # default "2 Jan 2006"
    # See the format reference https://gohugo.io/functions/format/#hugo-date-and-time-templating-reference

    # path name excluded from documentation menu
    menu_exclusion = [
        "archives",
        "archive",
        "blog",
        "entry",
        "post",
        "posts",
    ]

    # Algolia site search section
    # See https://www.algolia.com/doc/
    algolia_search_enable = true
    algolia_indexName = "hugo-demo-techdoc"
    algolia_appId = "7W4SAN4PLK"
    algolia_apiKey = "cbf12a63ff72d9c5dc0c10c195cf9128" # Search-Only API Key

# Global menu section
# See https://gohugo.io/content-management/menus/
[menu]
    [[menu.main]]
        name = "Home"
        url = "/"
        weight = 1

    [[menu.main]]
        name = "GitHub"
        url = "https://github.com/p4gefau1t"
        weight = 2

# Markup configure section
# See https://gohugo.io/getting-started/configuration-markup/
[markup]
    defaultMarkdownHandler = "goldmark"
    [markup.goldmark.renderer]
        unsafe= true
    [markup.tableOfContents]
        startLevel = 2
        endLevel = 4
        ordered = false

# Algolia Search configure section
[outputFormats.Algolia]
    baseName = "algolia"
    isPlainText = true
    mediaType = "application/json"
    notAlternative = true

[params.algolia]
    vars = [
        "title",
        "summary",
        "content",
        "date",
        "publishdate",
        "description",
        "permalink",
        "keywords",
        "lastmod",
    ]
    params = [
        "tags",
        "categories",
    ]


================================================
FILE: docs/content/_index.md
================================================
---
title: "简介"
draft: false
weight: 10
---

# Trojan-Go

这里是Trojan-Go的文档,你可以在左侧的导航栏中找到一些使用技巧,以及完整的配置文件说明。

Trojan-Go是使用Go语言实现的完整的Trojan代理,和Trojan协议以及原版的配置文件格式兼容。支持并且兼容Trojan-GFW版本的绝大多数功能,并扩展了更多的实用功能。

Trojan-Go的的首要目标是保障传输安全性和隐蔽性。在此前提下,尽可能提升传输性能和易用性。

如果你遇到配置和使用方面的问题,发现了软件Bug,或是有更好的想法,欢迎加入Trojan-Go的[Telegram交流反馈群](https://t.me/trojan_go_chat)。

----

> Across the Great Wall, we can reach every corner in the world.
>
> (越过长城,走向世界。)


================================================
FILE: docs/content/advance/_index.md
================================================
---
title: "高级配置"
draft: false
weight: 30
---

这一部分内容将介绍更复杂的Trojan-Go配置方法。对于与Trojan原版不兼容的特性,将在小节中明确标明。


================================================
FILE: docs/content/advance/aead.md
================================================
---
title: "使用Shadowsocks AEAD进行二次加密"
draft: false
weight: 8
---

### 注意,Trojan不支持这个特性

Trojan协议本身无加密,其安全性依赖于下层的TLS。在一般情况下,TLS安全性很好,并不需要再次加密Trojan流量。但是,某些场景下,你可能无法保证TLS隧道的安全性:

- 你使用了Websocket,经过不可信的CDN进行中转(如国内CDN)

- 你与服务器的连接遭到了GFW针对TLS的中间人攻击

- 你的证书失效,无法验证证书有效性

- 你使用了无法保证密码学安全的可插拔传输层

等等。

Trojan-Go支持使用Shadowsocks AEAD对Trojan-Go进行加密。其本质是在Trojan协议下方加上一层Shadowsocks AEAD加密。服务端和客户端必须同时开启,且密码和加密方式必须一致,否则无法进行通讯。

要开启AEAD加密,只需添加一个```shadowsocks```选项:

```json
...
"shadowsocks": {
    "enabled": true,
    "method": "AES-128-GCM",
    "password": "1234567890"
}
```

```method```如果省略,则默认使用AES-128-GCM。更多信息,参见“完整的配置文件”一节。


================================================
FILE: docs/content/advance/api.md
================================================
---
title: "使用API动态管理用户"
draft: false
weight: 10
---

### 注意,Trojan不支持这个特性

Trojan-Go使用gRPC提供了一组API,API支持以下功能:

- 用户信息增删改查

- 流量统计

- 速度统计

- IP连接数统计

Trojan-Go本身集成了API控制功能,也即可以使用一个Trojan-Go实例控制另一个Trojan-Go服务器。

你需要在你需要被控制的服务端配置添加API设置,例如:

```json
{
    ...
    "api": {
        "enabled": true,
        "api_addr": "127.0.0.1",
        "api_port": 10000,
    }
}
```

然后启动Trojan-Go服务器

```shell
./trojan-go -config ./server.json
```

然后可以使用另一个Trojan-Go连接该服务器进行管理,基本命令格式为

```shell
./trojan-go -api-addr SERVER_API_ADDRESS -api COMMAND
```

其中```SERVER_API_ADDRESS```为API地址和端口,如127.0.0.1:10000

```COMMAND```为API命令,合法的命令有

- list 列出所有用户

- get 获取某个用户信息

- set 设置某个用户信息(添加/删除/修改)

下面是一些例子

1. 列出所有用户信息

    ```shell
    ./trojan-go -api-addr 127.0.0.1:10000 -api list
    ```

    所有的用户信息将以json的形式导出,信息包括在线IP数量,实时速度,总上传和下载流量等。下面是一个返回的结果的例子

    ```json
    [{"user":{"hash":"d63dc919e201d7bc4c825630d2cf25fdc93d4b2f0d46706d29038d01"},"status":{"traffic_total":{"upload_traffic":36393,"download_traffic":186478},"speed_current":{"upload_speed":25210,"download_speed":72384},"speed_limit":{"upload_speed":5242880,"download_speed":5242880},"ip_limit":50}}]
    ```

    流量单位均为字节。

2. 获取一个用户信息

    可以使用 -target-password 指定密码,也可以使用 -target-hash 指定目标用户密码的SHA224散列值。格式和list命令相同

    ```shell
    ./trojan-go -api-addr 127.0.0.1:10000 -api get -target-password password
    ```

    或者

    ```shell
    ./trojan-go -api-addr 127.0.0.1:10000 -api get -target-hash d63dc919e201d7bc4c825630d2cf25fdc93d4b2f0d46706d29038d01
    ```

    以上两条命令等价,下面的例子统一使用明文密码的方式,散列值指定某个用户的方式以此类推。

    该用户信息将以json的形式导出,格式与list命令类似。下面是一个返回的结果的例子

    ```json
    {"user":{"hash":"d63dc919e201d7bc4c825630d2cf25fdc93d4b2f0d46706d29038d01"},"status":{"traffic_total":{"upload_traffic":36393,"download_traffic":186478},"speed_current":{"upload_speed":25210,"download_speed":72384},"speed_limit":{"upload_speed":5242880,"download_speed":5242880},"ip_limit":50}}
    ```

3. 添加一个用户信息

    ```shell
    ./trojan-go -api-addr 127.0.0.1:10000 -api set -add-profile -target-password password
    ```

4. 删除一个用户信息

    ```shell
    ./trojan-go -api-addr 127.0.0.1:10000 -api set -delete-profile -target-password password
    ```

5. 修改一个用户信息

    ```shell
    ./trojan-go -api-addr 127.0.0.1:10000 -api set -modify-profile -target-password password \
        -ip-limit 3 \
        -upload-speed-limit 5242880 \
        -download-speed-limit 5242880
    ```

    这个命令将密码为password的用户上传和下载速度限制为5MiB/s,同时连接的IP数量限制为3个,注意这里5242880的单位是字节。如果填写0或者负数,则表示不进行限制。


================================================
FILE: docs/content/advance/customize-protocol-stack.md
================================================
---
title: "自定义协议栈"
draft: false
weight: 8
---

### 注意,Trojan不支持这个特性

Trojan-Go允许高级用户自定义协议栈。在自定义模式下,Trojan-Go将放弃对协议栈的控制,允许用户操作底层协议栈组合。例如

- 在一层TLS上再建立一层或更多层TLS加密

- 使用TLS传输Websocket流量,在Websocket层上再建立一层TLS,在第二层TLS上再使用Shadowsocks AEAD进行加密传输

- 在TCP连接上,使用Shadowsocks的AEAD加密传输Trojan协议

- 将一个入站Trojan的TLS流量解包后重新用TLS包装为新的出站Trojan流量

等等。

**如果你不了解网络相关知识,请不要尝试使用这个功能。不正确的配置可能导致Trojan-Go无法正常工作,或是导致性能和安全性方面的问题。**

Trojan-Go将所有协议抽象为隧道,每个隧道可能提供客户端,负责发送;也可能提供服务端,负责接受;或者两者皆提供。自定义协议栈即自定义隧道的堆叠方式。

### 在继续配置之前,请先阅读开发指南中“基本介绍”一节,确保已经理解Trojan-Go运作方式

下面是Trojan-Go支持的隧道和他们的属性:

| 隧道        | 需要下层提供流 | 需要下层提供包 | 向上层提供流 | 向上层提供包 | 可以作为入站 | 可以作为出站 |
| ----------- | -------------- | -------------- | ------------ | ------------ | ------------ | ------------ |
| transport   | n              | n              | y            | y            | y            | y            |
| dokodemo    | n              | n              | y            | y            | y            | n            |
| tproxy      | n              | n              | y            | y            | y            | n            |
| tls         | y              | n              | y            | n            | y            | y            |
| trojan      | y              | n              | y            | y            | y            | y            |
| mux         | y              | n              | y            | n            | y            | y            |
| simplesocks | y              | n              | y            | y            | y            | y            |
| shadowsocks | y              | n              | y            | n            | y            | y            |
| websocket   | y              | n              | y            | n            | y            | y            |
| freedom     | n              | n              | y            | y            | n            | y            |
| socks       | y              | y              | y            | y            | y            | n            |
| http        | y              | n              | y            | n            | y            | n            |
| router      | y              | y              | y            | y            | n            | y            |
| adapter     | n              | n              | y            | y            | y            | n            |

自定义协议栈的工作方式是,定义树/链上节点并分别它们起名(tag)并添加配置,然后使用tag组成的有向路径,描述这棵树/链。例如,对于一个典型的Trojan-Go服务器,可以如此描述:

入站,一共两条路径,tls节点将自动识别trojan和websocket流量并进行分发

- transport->tls->trojan

- transport->tls->websocket->trojan

出站,只能有一条路径

- router->freedom

对于入站,从根开始描述多条路径,组成一棵**多叉树**(也可以退化为一条链),不满足树性质的图将导致未定义的行为;对于出站,必须描述一条**链**。

每条路径必须满足这样的条件:

1. 必须以**不需要下层提供流或包**的隧道开始(transport/adapter/tproxy/dokodemo等)

2. 必须以**能向上层提供包和流**的隧道终止(trojan/simplesocks/freedom等)

3. 出站单链上,隧道必须都可作为出站。入站的所有路径上,隧道必须都可作为入站。

要启用自定义协议栈,将```run_type```指定为custom,此时除```inbound```和```outbound```之外的其他选项将被忽略。

下面是一个例子,你可以在此基础上插入或减少协议节点。配置文件为简明起见,使用YAML进行配置,你也可以使用JSON来配置,除格式不同之外,效果是等价的。

客户端 client.yaml

```yaml
run-type: custom

inbound:
  node:
    - protocol: adapter
      tag: adapter
      config:
        local-addr: 127.0.0.1
        local-port: 1080
    - protocol: socks
      tag: socks
      config:
        local-addr: 127.0.0.1
        local-port: 1080
  path:
    -
      - adapter
      - socks

outbound:
  node:
    - protocol: transport
      tag: transport
      config:
        remote-addr: you_server
        remote-port: 443

    - protocol: tls
      tag: tls
      config:
        ssl:
          sni: localhost
          key: server.key
          cert: server.crt

    - protocol: trojan
      tag: trojan
      config:
        password:
          - 12345678

  path:
    -
      - transport
      - tls
      - trojan

```

服务端 server.yaml

```yaml
run-type: custom

inbound:
  node:
    - protocol: websocket
      tag: websocket
      config:
        websocket:
            enabled: true
            hostname: example.com
            path: /ws

    - protocol: transport
      tag: transport
      config:
        local-addr: 0.0.0.0
        local-port: 443
        remote-addr: 127.0.0.1
        remote-port: 80

    - protocol: tls
      tag: tls
      config:
        remote-addr: 127.0.0.1
        remote-port: 80
        ssl:
          sni: localhost
          key: server.key
          cert: server.crt

    - protocol: trojan
      tag: trojan1
      config:
        remote-addr: 127.0.0.1
        remote-port: 80
        password:
          - 12345678

    - protocol: trojan
      tag: trojan2
      config:
        remote-addr: 127.0.0.1
        remote-port: 80
        password:
          - 87654321

  path:
    -
      - transport
      - tls
      - trojan1
    -
      - transport
      - tls
      - websocket
      - trojan2

outbound:
  node:
    - protocol: freedom
      tag: freedom

  path:
    -
      - freedom
```


================================================
FILE: docs/content/advance/forward.md
================================================
---
title: "隧道和反向代理"
draft: false
weight: 5
---

你可以使用Trojan-Go建立隧道。一个典型的应用是,使用Trojan-Go在本地建立一个无污染的DNS服务器,下面是一个配置的例子

```json
{
    "run_type": "forward",
    "local_addr": "127.0.0.1",
    "local_port": 53,
    "remote_addr": "your_awesome_server",
    "remote_port": 443,
    "target_addr": "8.8.8.8",
    "target_port": 53,
    "password": [
        "your_awesome_password"
    ]
}
```

forward本质上是一个客户端,不过你需要填入```target_addr```和```target_port```字段,指明反向代理的目标。

使用这份配置文件后,本地53的TCP和UDP端口将被监听,所有的向本地53端口发送的TCP或者UDP数据,都会通过TLS隧道转发给远端服务器your_awesome_server,远端服务器得到回应后,数据会通过隧道返回到本地53端口。 也就是说,你可以将127.0.0.1当作一个DNS服务器,本地查询的结果和远端服务器查询的结果是一致的。你可以使用这个配置避开DNS污染。

同样的原理,你可以在本地搭建一个Google的镜像

```json
{
    "run_type": "forward",
    "local_addr": "127.0.0.1",
    "local_port": 443,
    "remote_addr": "your_awesome_server",
    "remote_port": 443,
    "target_addr": "www.google.com",
    "target_port": 443,
    "password": [
        "your_awesome_password"
    ]
}
```

访问```https://127.0.0.1```即可访问谷歌主页,但是注意这里由于谷歌服务器提供的https证书是google.com的证书,而当前域名为127.0.0.1,因此浏览器会引发一个证书错误的警告。

类似的,可以使用forward传输其他代理协议。例如,使用Trojan-Go传输shadowsocks的流量,远端主机开启ss服务器,监听127.0.0.1:12345,并且远端服务器在443端口开启了正常的Trojan-Go服务器。你可以如此指定配置

```json
{
    "run_type": "forward",
    "local_addr": "0.0.0.0",
    "local_port": 54321,
    "remote_addr": "your_awesome_server",
    "remote_port": 443,
    "target_addr": "www.google.com",
    "target_port": 12345,
    "password": [
        "your_awesome_password"
    ]
}
```

此后,任何连接本机54321端口的TCP/UDP连接,等同于连接远端12345端口。你可以使用shadowsocks客户端连接本地的54321端口,ss流量将使用trojan的隧道连接传输至远端12345端口的ss服务器。


================================================
FILE: docs/content/advance/mux.md
================================================
---
title: "启用多路复用提升网络并发性能"
draft: false
weight: 1
---

### 注意,Trojan不支持这个特性

Trojan-Go支持使用多路复用提升网络并发性能。

Trojan协议基于TLS。在一个TLS安全连接建立之前,连接双方需要进行密钥协商和交换等步骤确保后续通讯的安全性。这个过程即为TLS握手。

目前GFW对于TLS握手存在审查和干扰,同时由于出口网络拥塞的原因,普通的线路完成TLS握手通常需要将近一秒甚至更长的时间。这可能导致浏览网页和观看视频的延迟提高。

Trojan-Go使用多路复用的方式解决这一问题。每个建立的TLS连接将承载多个TCP连接。当新的代理请求到来时,不需要和服务器握手发起一个新的TLS连接,而是尽可能重复使用已有的TLS连接。以此减少频繁TLS握手和TCP握手的带来的延迟。

启用多路复用不会增加你的链路速度(甚至会有所减少),而且可能会增加服务器和客户端的计算负担。可以粗略地理解为,多路复用牺牲网络吞吐和CPU功耗,换取更低的延迟。在高并发的情景下,如浏览含有大量图片的网页时,或者发送大量UDP请求时,可以提升使用体验。

激活```mux```模块,只需要将```mux```选项中```enabled```字段设为true即可,下面是一个客户端的例子

```json
...
"mux" :{
    "enabled": true
}
```

只需要配置客户端即可,服务端可以自动适配,无需配置```mux```选项。

完整的mux配置如下

```json
"mux": {
    "enabled": false,
    "concurrency": 8,
    "idle_timeout": 60
}
```

```concurrency```是每个TLS连接最多可以承载的TCP连接数。这个数值越大,每个TLS连接被复用的程度就更高,握手导致的延迟越低。但服务器和客户端的计算负担也会越大,这有可能使你的网络吞吐量降低。如果你的线路的TLS握手极端缓慢,你可以将这个数值设置为-1,Trojan-Go将只进行一次TLS握手,只使用唯一的一条TLS连接进行传输。

```idle_timeout```指的是每个TLS连接空闲多长时间后关闭。设置超时时间,**可能**有助于减少不必要的长连接存活确认(Keep Alive)流量传输引发GFW的探测。你可以将这个数值设置为-1,TLS连接在空闲时将被立即关闭。


================================================
FILE: docs/content/advance/nat.md
================================================
---
title: "透明代理"
draft: false
weight: 11
---

### 注意,Trojan不完全支持这个特性(UDP)

Trojan-Go支持基于tproxy的透明TCP/UDP代理。

要开启透明代理模式,将一份正确的客户端配置(配置方式参见基本配置部分)其中的```run_type```修改为```nat```,并按照需求修改本地监听端口即可。

之后需要添加iptables规则。这里假定你的网关具有两个网卡,下面这份配置将其中一个网卡(局域网)的入站包转交给Trojan-Go,由Trojan-Go通过隧道,通过另一个网卡(互联网)发送到远端Trojan-Go服务器。你需要将下面的```$SERVER_IP```,```$TROJAN_GO_PORT```,```$INTERFACE```替换为自己的配置。

```shell
# 新建TROJAN_GO链
iptables -t mangle -N TROJAN_GO

# 绕过Trojan-Go服务器地址
iptables -t mangle -A TROJAN_GO -d $SERVER_IP -j RETURN

# 绕过私有地址
iptables -t mangle -A TROJAN_GO -d 0.0.0.0/8 -j RETURN
iptables -t mangle -A TROJAN_GO -d 10.0.0.0/8 -j RETURN
iptables -t mangle -A TROJAN_GO -d 127.0.0.0/8 -j RETURN
iptables -t mangle -A TROJAN_GO -d 169.254.0.0/16 -j RETURN
iptables -t mangle -A TROJAN_GO -d 172.16.0.0/12 -j RETURN
iptables -t mangle -A TROJAN_GO -d 192.168.0.0/16 -j RETURN
iptables -t mangle -A TROJAN_GO -d 224.0.0.0/4 -j RETURN
iptables -t mangle -A TROJAN_GO -d 240.0.0.0/4 -j RETURN

# 未命中上文的规则的包,打上标记
iptables -t mangle -A TROJAN_GO -j TPROXY -p tcp --on-port $TROJAN_GO_PORT --tproxy-mark 0x01/0x01
iptables -t mangle -A TROJAN_GO -j TPROXY -p udp --on-port $TROJAN_GO_PORT --tproxy-mark 0x01/0x01

# 从$INTERFACE网卡流入的所有TCP/UDP包,跳转TROJAN_GO链
iptables -t mangle -A PREROUTING -p tcp -i $INTERFACE -j TROJAN_GO
iptables -t mangle -A PREROUTING -p udp -i $INTERFACE -j TROJAN_GO

# 添加路由,打上标记的包重新进入本地回环
ip route add local default dev lo table 100
ip rule add fwmark 1 lookup 100
```

配置完成后**以root权限启动**Trojan-Go客户端:

```shell
sudo trojan-go
```


================================================
FILE: docs/content/advance/nginx-relay.md
================================================
---
title: "一种基于SNI代理的多路径分流中继方案"
draft: false
weight: 6
---

## 前言

Trojan 是一种通过 TLS 封装后进行加密数据传输的工具,利用其 TLS 的特性,我们可以通过 SNI 代理实现在同一主机端口上实现不同路径的分流中继。

## 所需工具及其他准备

- 中继机:nginx 1.11.5 及以上版本
- 落地机:trojan 服务端(无版本要求)

## 配置方法

为了便于说明,这里使用了两台中继主机和两台落地主机。  
四台主机所绑定的域名分别为 (a/b/c/d).example.com。如图所示。  
相互连接一共4条路径。分别为 a-c、a-d、b-c、b-d 。

```text
                        +-----------------+           +--------------------+
                        |                 +---------->+                    |
                        |   VPS RELAY A   |           |   VPS ENDPOINT C   |
                  +---->+                 |   +------>+                    |
                  |     |  a.example.com  |   |       |   c.example.com    |
                  |     |                 +------+    |                    |
  +----------+    |     +-----------------+   |  |    +--------------------+
  |          |    |                           |  |
  |  client  +----+                           |  |
  |          |    |                           |  |
  +----------+    |     +-----------------+   |  |    +--------------------+
                  |     |                 |   |  |    |                    |
                  |     |   VPS RELAY B   |   |  +--->+   VPS ENDPOINT D   |
                  +---->+                 +---+       |                    |
                        |  b.example.com  |           |   d.example.com    |
                        |                 +---------->+                    |
                        +-----------------+           +--------------------+
```

### 配置路径域名和相应的证书

首先我们需要将每条路径分别分配一个域名,并使其解析到分别的入口主机上。  

```text
a-c.example.com CNAME a.example.com  
a-d.example.com CNAME a.example.com  
b-c.example.com CNAME b.example.com  
b-d.example.com CNAME b.example.com
```

然后我们需要在落地主机上部署所有目标路径的证书  
由于解析记录和主机 IP 不符,HTTP 验证无法通过。这里建议使用 DNS 验证方式签发证书。  
具体 DNS 验证插件需要根据您的域名 DNS 解析托管商选择,这里使用了 AWS Route 53。  

```shell
certbot certonly --dns-route53 -d a-c.example.com -d b-c.example.com // 主机 C 上
certbot certonly --dns-route53 -d a-d.example.com -d b-d.example.com // 主机 D 上
```

### 配置 SNI 代理

这里我们使用 nginx 的 ssl_preread 模块实现 SNI 代理。  
请安装 nginx 后按如下方法修过 nginx.conf 文件。  
注意这里不是 HTTP 服务,请不要写在虚拟主机的配置中。

这里给出主机 A 上的对应配置,主机 B 同理。

```nginx
stream {
  map $ssl_preread_server_name $name {
    a-c.example.com   c.example.com;  # 将 a-c 路径流量转发至主机 C
    a-d.example.com   d.example.com;  # 将 a-d 路径流量转发至主机 D

    # 如果此主机上需要配置其他占用 443 端口的服务 (例如 web 服务和 Trojan 服务)
    # 请使那些服务监听在其他本地端口(这里使用了 4000)
    # 所有不匹配上方 SNI 的 TLS 请求都会转发至此端口,如不需要可以删除此行
    default           localhost:4000;
  }

  server {
    listen      443; # 监听 443 端口
    proxy_pass  $name;
    ssl_preread on;
  }
}
```

### 配置落地 Trojan 服务

在之前的配置中我们使用了一个证书签发了所有目标路径的域名,所以这里我们可以使用一个 Trojan 服务端处理所有目标路径的请求。  
Trojan 的配置和通常配置方法无异,这里还是提供一份例子。无关的配置已省略。

```json
{
    "run_type": "server",
    "local_addr": "0.0.0.0",
    "local_port": 443,
    "ssl": {
        "cert": "/path/to/certificate.crt",
        "key": "/path/to/private.key",
    }
    ...
}
```

小提示:如果需要在落地主机上对不同路径分别使用独立的 Trojan 服务端(比如需要分别接入各自的计费服务),可以在落地机上再配置一个 SNI 代理,并分别转发至本地不同的 Trojan 服务端监听端口。由于配置与前面所提到的过程基本相同,这里便不再赘述。

## 总结

通过以上介绍的配置方法,我们可以在单一端口上实现多入口多出口多级中继的 Trojan 流量转发。  
对于多级中继,只需在中间节点上按相同思路配置 SNI 代理即可。


================================================
FILE: docs/content/advance/plugin.md
================================================
---
title: "使用Shadowsocks插件/可插拔传输层"
draft: false
weight: 7
---

### 注意,Trojan不支持这个特性

Trojan-Go支持可插拔的传输层。原则上,Trojan-Go可以使用任何有TCP隧道功能的软件作为传输层,如v2ray、shadowsocks、kcp等。同时,Trojan-Go也兼容Shadowsocks的SIP003插件标准,如GoQuiet,v2ray-plugin等。也可以使用Tor的传输层插件,如obfs4,meek等。

你可以使用这些插件,替换Trojan-Go的TLS传输层。

开启可插拔传输层插件后,Trojan-Go客户端将会把**流量明文**直接传输给客户端本地的插件处理。由客户端插件负责进行加密和混淆,并将流量传输给服务端的插件。服务端的插件接收到流量,进行解密和解析,将**流量明文**传输给服务端本地的Trojan-Go服务端。

你可以使用任何插件对流量进行加密和混淆,只需添加"transport_plugin"选项,并指定插件的可执行文件的路径,并做好配置即可。

我们更建议**自行设计协议并开发相应插件**。因为目前现有的所有插件无法对接Trojan-Go的对抗主动探测的特性,而且部分插件并无加密能力。如果你对开发插件有兴趣,欢迎在"实现细节和开发指南"一节中查看插件设计的指南。

例如,可以使用符合SIP003标准的v2ray-plugin,下面是一个例子:

**这个配置中使用了websocket明文传输未经加密的trojan协议,存在安全隐患。这个配置仅作为演示使用。**

**不要在任何情况下使用这个配置穿透GFW。**

服务端配置:

```json
...(省略)
"transport_plugin": {
    "enabled": true,
    "type": "shadowsocks",
    "command": "./v2ray-plugin",
    "arg": ["-server", "-host", "www.baidu.com"]
}
```

客户端配置:

```json
...(省略)
"transport_plugin": {
    "enabled": true,
    "type": "shadowsocks",
    "command": "./v2ray-plugin",
    "arg": ["-host", "www.baidu.com"]
}
```

注意,v2ray-plugin插件需要指定```-server```参数来区分客户端和服务端。更多关于该插件详细的说明,参考v2ray-plugin的文档。

启动Trojan-Go后,你可以看到v2ray-plugin启动的输出。插件将把流量伪装为Websocket流量并传输。

非SIP003标准的插件可能需要不同的配置,你可以指定```type```为"other",并自行指定插件地址,插件启动参数、环境变量。


================================================
FILE: docs/content/advance/router.md
================================================
---
title: "国内直连和广告屏蔽"
draft: false
weight: 3
---

### 注意,Trojan不支持这个特性

Trojan-Go内建的路由模块可以帮助你实现国内直连,即客户端对于国内网站不经过代理,直接连接。

路由模块在客户端可以配置三种策略(```bypass```, ```proxy```, ```block```),在服务端只可使用```block```策略。

下面是一个例子

```json
{
    "run_type": "client",
    "local_addr": "127.0.0.1",
    "local_port": 1080,
    "remote_addr": "your_server",
    "remote_port": 443,
    "password": [
        "your_password"
    ],
    "ssl": {
        "sni": "your-domain-name.com"
    },
    "mux" :{
        "enabled": true
    },
    "router":{
        "enabled": true,
        "bypass": [
            "geoip:cn",
            "geoip:private",
            "geosite:cn",
            "geosite:geolocation-cn"
        ],
        "block": [
            "geosite:category-ads"
        ],
        "proxy": [
            "geosite:geolocation-!cn"
        ]
    }
}
```

这个配置文件激活了router模块,使用的是白名单的模式,当匹配到中国大陆或者局域网的IP/域名时,直接连接。如果是广告运营商的域名,则直接断开连接。

所需要的数据库```geoip.dat```和```geosite.dat```已经包含在release的压缩包中,直接使用即可。它们来自V2Ray的[domain-list-community](https://github.com/v2fly/domain-list-community)和[geoip](https://github.com/v2fly/geoip)。

你可以使用如```geosite:cn```、```geosite:geolocation-!cn```、```geosite:category-ads-all```、```geosite:bilibili```的形式来指定某一类域名,所有可用的tag可以在[domain-list-community](https://github.com/v2fly/domain-list-community)仓库的[```data```](https://github.com/v2fly/domain-list-community/tree/master/data)目录中找到。```geosite.dat``` 更详细使用说明,参考[V2Ray/Routing路由#预定义域名列表](https://www.v2fly.org/config/routing.html#预定义域名列表)。

你可以使用如```geoip:cn```、```geoip:hk```、```geoip:us```、```geoip:private```的形式来指定某一类IP。`geoip:private`为特殊项,囊括了内网IP和保留IP,其余类别囊括了各个国家/地区的IP地址段。各国家/地区的代号参考[维基百科](https://zh.wikipedia.org/wiki/%E5%9C%8B%E5%AE%B6%E5%9C%B0%E5%8D%80%E4%BB%A3%E7%A2%BC)。

你也可以配置自己的路由规则。例如,想要屏蔽所有example.com域名以及其子域名,以及192.168.1.0/24,添加下面的规则。

```json
"block": [
    "domain:example.com",
    "cidr:192.168.1.0/24"
]
```

支持的格式有

- "domain:",子域名匹配

- "full:",完全域名匹配

- "regexp:",正则表达式匹配

- "cidr:",CIDR匹配

更详细的说明参考"完整的配置文件"一节。


================================================
FILE: docs/content/advance/websocket.md
================================================
---
title: "使用Websocket进行CDN转发和抵抗中间人攻击"
draft: false
weight: 2
---

### 注意,Trojan不支持这个特性

Trojan-Go支持使用TLS+Websocket承载Trojan协议,使得利用CDN进行流量中转成为可能。

Trojan协议本身不带加密,安全性依赖外层的TLS。但流量一旦经过CDN,TLS对CDN是透明的。其服务提供者可以对TLS的明文内容进行审查。**如果你使用的是不可信任的CDN(任何在中国大陆注册备案的CDN服务均应被视为不可信任),请务必开启Shadowsocks AEAD对Webosocket流量进行加密,以避免遭到识别和审查。**

服务器和客户端配置文件中同时添加websocket选项,并将其```enabled```字段设置为true,并填写```path```字段和```host```字段即可启用Websocket支持。下面是一个完整的Websocket选项:

```json
"websocket": {
    "enabled": true,
    "path": "/your-websocket-path",
    "host": "example.com"
}
```

```host```是主机名,一般填写域名。客户端```host```是可选的,填写你的域名。如果留空,将会使用```remote_addr```填充。

```path```指的是websocket所在的URL路径,必须以斜杠("/")开始。路径并无特别要求,满足URL基本格式即可,但要保证客户端和服务端的```path```一致。```path```应当选择较长的字符串,以避免遭到GFW直接的主动探测。

客户端的```host```将包含在Websocket的握手HTTP请求中,发送给CDN服务器,必须有效;服务端和客户端```path```必须一致,否则Websocket握手无法进行。

下面是一个客户端配置文件的例子

```json
{
    "run_type": "client",
    "local_addr": "127.0.0.1",
    "local_port": 1080,
    "remote_addr": "www.your_awesome_domain_name.com",
    "remote_port": 443,
    "password": [
        "your_password"
    ],
    "websocket": {
        "enabled": true,
        "path": "/your-websocket-path",
        "host": "example.com"
    },
    "shadowsocks": {
        "enabled": true,
        "password": "12345678"
    }
}
```


================================================
FILE: docs/content/basic/_index.md
================================================
---
title: "基本配置"
draft: false
weight: 20
---

这一部分内容将介绍如何配置基本的Trojan-Go代理服务器和客户端。


================================================
FILE: docs/content/basic/config.md
================================================
---
title: "正确配置Trojan-Go"
draft: false
weight: 22
---

下面将介绍如何正确配置Trojan-Go以完全隐藏你的代理节点特征。

在开始之前,你需要

- 一个服务器,且未被GFW封锁

- 一个域名,可以使用免费的域名服务,如.tk等

- Trojan-Go,可以从release页面下载

- 证书和密钥,可以从letsencrypt等机构免费申请签发

### 服务端配置

我们的目标是,使得你的服务器和正常的HTTPS网站表现相同。

首先你需要一个HTTP服务器,可以使用nginx,apache,caddy等配置一个本地HTTP服务器,也可以使用别人的HTTP服务器。HTTP服务器的作用是,当GFW主动探测时,向它展示一个完全正常的Web页面。

**你需要在```remote_addr```和```remote_port```指定这个HTTP服务器的地址。```remote_addr```可以是IP或者域名。Trojan-Go将会测试这个HTTP服务器是否工作正常,如果不正常,Trojan-Go会拒绝启动。**

下面是一份比较安全的服务器配置server.json,需要你在本地80端口配置一个HTTP服务(必要,你也可以使用其他的网站HTTP服务器,如"remote_addr": "example.com"),在1234端口配置一个HTTPS服务,或是一个展示"400 Bad Request"的静态HTTP网页服务。(可选,可以删除```fallback_port```字段,跳过这个步骤)

```json
{
    "run_type": "server",
    "local_addr": "0.0.0.0",
    "local_port": 443,
    "remote_addr": "127.0.0.1",
    "remote_port": 80,
    "password": [
        "your_awesome_password"
    ],
    "ssl": {
        "cert": "server.crt",
        "key": "server.key",
        "fallback_port": 1234
    }
}
```

这个配置文件使Trojan-Go在服务器的所有IP地址上(0.0.0.0)监听443端口,分别使用server.crt和server.key作为证书和密钥进行TLS握手。你应该使用尽可能复杂的密码,同时确保客户端和服务端```password```是一致的。注意,**Trojan-Go会检测你的HTTP服务器```http://remote_addr:remote_port```是否正常工作。如果你的HTTP服务器工作不正常,Trojan-Go将拒绝启动。**

当一个客户端试图连接Trojan-Go的监听端口时,会发生下面的事情:

- 如果TLS握手成功,检测到TLS的内容非Trojan协议(有可能是HTTP请求,或者来自GFW的主动探测)。Trojan-Go将TLS连接代理到本地127.0.0.1:80上的HTTP服务。这时在远端看来,Trojan-Go服务就是一个HTTPS网站。

- 如果TLS握手成功,并且被确认是Trojan协议头部,并且其中的密码正确,那么服务器将解析来自客户端的请求并进行代理,否则和上一步的处理方法相同。

- 如果TLS握手失败,说明对方使用的不是TLS协议进行连接。此时Trojan-Go将这个TCP连接代理到本地127.0.0.1:1234上运行的HTTPS服务(或者HTTP服务),返回一个展示400 Bad Reqeust的HTTP页面。```fallback_port```是一个可选选项,如果没有填写,Trojan-Go会直接终止连接。虽然是可选的,但是还是强烈建议填写。

你可以通过使用浏览器访问你的域名```https://your-domain-name.com```来验证。如果工作正常,你的浏览器会显示一个正常的HTTPS保护的Web页面,页面内容与服务器本机80端口上的页面一致。你还可以使用```http://your-domain-name.com:443```验证```fallback_port```工作是否正常。

事实上,你甚至可以将Trojan-Go当作你的HTTPS服务器,用来给你的网站提供HTTPS服务。访客可以正常地通过Trojan-Go浏览你的网站,而和代理流量互不影响。但是注意,不要在```remote_port```和```fallback_port```搭建有高实时性需求的服务,Trojan-Go识别到非Trojan协议流量时会有意增加少许延迟以抵抗GFW基于时间的检测。

配置完成后,可以使用

```shell
./trojan-go -config ./server.json
```

启动服务端。

### 客户端配置

对应的客户端配置client.json

```json
{
    "run_type": "client",
    "local_addr": "127.0.0.1",
    "local_port": 1080,
    "remote_addr": "your_awesome_server",
    "remote_port": 443,
    "password": [
        "your_awesome_password"
    ],
    "ssl": {
        "sni": "your-domain-name.com"
    }
}
```

这个客户端配置使Trojan-Go开启一个监听在本地1080端口的socks5/http代理(自动识别),远端服务器为your_awesome_server:443,your_awesome_server可以是IP或者域名。

如果你在```remote_addr```中填写的是域名,```sni```可以省略。如果你在```remote_addr```填写的是IP地址,```sni```字段应当填写你申请证书的对应域名,或者你自己签发的证书的Common Name,而且必须一致。注意,```sni```字段目前的在TLS协议中是**明文传送**的(目的是使服务器提供相应证书)。GFW已经被证实具有SNI探测和阻断能力,所以不要填写类似```google.com```等已经被封锁的域名,否则很有可能导致你的服务器也被遭到封锁。

配置完成后,可以使用

```shell
./trojan-go -config ./client.json
```

启动客户端。

更多关于配置文件的信息,可以在左侧导航栏中找到相应介绍。


================================================
FILE: docs/content/basic/full-config.md
================================================
---
title: "完整的配置文件"
draft: false
weight: 30
---

下面是一个完整的配置文件,其中的必填选项有

- ```run_type```

- ```local_addr```

- ```local_port```

- ```remote_addr```

- ```remote_port```

对于服务器```server```,```key```和```cert```为必填。

对于客户端```client```,反向代理隧道```forward```,以及透明代理```nat```,```password```必填

其余未填的选项,用下面给出的值进行填充。

*Trojan-Go支持对人类更友好的YAML语法,配置文件的基本结构与JSON相同,效果等价。但是为了遵守YAML的命名习惯,你需要把下划线("_")转换为横杠("-"),如```remote_addr```在YAML文件中为```remote-addr```*

```json
{
  "run_type": *required*,
  "local_addr": *required*,
  "local_port": *required*,
  "remote_addr": *required*,
  "remote_port": *required*,
  "log_level": 1,
  "log_file": "",
  "password": [],
  "disable_http_check": false,
  "udp_timeout": 60,
  "ssl": {
    "verify": true,
    "verify_hostname": true,
    "cert": *required*,
    "key": *required*,
    "key_password": "",
    "cipher": "",
    "curves": "",
    "prefer_server_cipher": false,
    "sni": "",
    "alpn": [
      "http/1.1"
    ],
    "session_ticket": true,
    "reuse_session": true,
    "plain_http_response": "",
    "fallback_addr": "",
    "fallback_port": 0,
    "fingerprint": ""
  },
  "tcp": {
    "no_delay": true,
    "keep_alive": true,
    "prefer_ipv4": false
  },
  "mux": {
    "enabled": false,
    "concurrency": 8,
    "idle_timeout": 60
  },
  "router": {
    "enabled": false,
    "bypass": [],
    "proxy": [],
    "block": [],
    "
Download .txt
gitextract_d00n_ui_/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   └── bug-report.md
│   ├── linters/
│   │   └── .golangci.yml
│   └── workflows/
│       ├── docker-build.yml
│       ├── docker-nightly-build.yml
│       ├── gh-pages.yml
│       ├── linter.yml
│       ├── nightly-build.yml
│       ├── release-build.yml
│       └── test.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── api/
│   ├── api.go
│   ├── control/
│   │   └── control.go
│   └── service/
│       ├── api.pb.go
│       ├── api.proto
│       ├── api_grpc.pb.go
│       ├── client.go
│       ├── client_test.go
│       ├── config.go
│       ├── gen.sh
│       ├── server.go
│       └── server_test.go
├── common/
│   ├── common.go
│   ├── error.go
│   ├── geodata/
│   │   ├── cache.go
│   │   ├── decode.go
│   │   ├── decode_test.go
│   │   ├── interface.go
│   │   └── loader.go
│   ├── io.go
│   ├── io_test.go
│   ├── net.go
│   └── sync.go
├── component/
│   ├── api.go
│   ├── base.go
│   ├── client.go
│   ├── custom.go
│   ├── forward.go
│   ├── mysql.go
│   ├── nat.go
│   ├── other.go
│   └── server.go
├── config/
│   ├── config.go
│   └── config_test.go
├── constant/
│   └── constant.go
├── docs/
│   ├── .gitignore
│   ├── Makefile
│   ├── archetypes/
│   │   └── default.md
│   ├── config.toml
│   └── content/
│       ├── _index.md
│       ├── advance/
│       │   ├── _index.md
│       │   ├── aead.md
│       │   ├── api.md
│       │   ├── customize-protocol-stack.md
│       │   ├── forward.md
│       │   ├── mux.md
│       │   ├── nat.md
│       │   ├── nginx-relay.md
│       │   ├── plugin.md
│       │   ├── router.md
│       │   └── websocket.md
│       ├── basic/
│       │   ├── _index.md
│       │   ├── config.md
│       │   ├── full-config.md
│       │   └── trojan.md
│       └── developer/
│           ├── _index.md
│           ├── api.md
│           ├── build.md
│           ├── mux.md
│           ├── overview.md
│           ├── plugin.md
│           ├── simplesocks.md
│           ├── trojan.md
│           ├── url.md
│           └── websocket.md
├── easy/
│   └── easy.go
├── example/
│   ├── client.json
│   ├── client.yaml
│   ├── server.json
│   ├── server.yaml
│   ├── trojan-go.service
│   └── trojan-go@.service
├── go.mod
├── go.sum
├── log/
│   ├── golog/
│   │   ├── buffer/
│   │   │   ├── buffer.go
│   │   │   └── buffer_test.go
│   │   ├── colorful/
│   │   │   ├── colorful.go
│   │   │   └── colorful_test.go
│   │   └── golog.go
│   ├── log.go
│   └── simplelog/
│       └── simplelog.go
├── main.go
├── option/
│   └── option.go
├── proxy/
│   ├── client/
│   │   ├── client.go
│   │   └── config.go
│   ├── config.go
│   ├── custom/
│   │   ├── config.go
│   │   └── custom.go
│   ├── forward/
│   │   └── forward.go
│   ├── nat/
│   │   ├── nat.go
│   │   └── nat_stub.go
│   ├── option.go
│   ├── proxy.go
│   ├── server/
│   │   ├── config.go
│   │   └── server.go
│   └── stack.go
├── redirector/
│   ├── redirector.go
│   └── redirector_test.go
├── statistic/
│   ├── memory/
│   │   ├── config.go
│   │   ├── memory.go
│   │   └── memory_test.go
│   ├── mysql/
│   │   ├── config.go
│   │   └── mysql.go
│   └── statistics.go
├── test/
│   ├── scenario/
│   │   ├── custom_test.go
│   │   └── proxy_test.go
│   └── util/
│       ├── target.go
│       └── util.go
├── tunnel/
│   ├── adapter/
│   │   ├── config.go
│   │   ├── server.go
│   │   └── tunnel.go
│   ├── dokodemo/
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── dokodemo_test.go
│   │   ├── server.go
│   │   └── tunnel.go
│   ├── freedom/
│   │   ├── client.go
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── freedom_test.go
│   │   └── tunnel.go
│   ├── http/
│   │   ├── http_test.go
│   │   ├── server.go
│   │   └── tunnel.go
│   ├── metadata.go
│   ├── mux/
│   │   ├── client.go
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── mux_test.go
│   │   ├── server.go
│   │   └── tunnel.go
│   ├── router/
│   │   ├── client.go
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── data.go
│   │   ├── router_test.go
│   │   └── tunnel.go
│   ├── shadowsocks/
│   │   ├── client.go
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── server.go
│   │   ├── shadowsocks_test.go
│   │   └── tunnel.go
│   ├── simplesocks/
│   │   ├── client.go
│   │   ├── conn.go
│   │   ├── server.go
│   │   ├── simplesocks_test.go
│   │   └── tunnel.go
│   ├── socks/
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── server.go
│   │   ├── socks_test.go
│   │   └── tunnel.go
│   ├── tls/
│   │   ├── client.go
│   │   ├── config.go
│   │   ├── fingerprint/
│   │   │   └── tls.go
│   │   ├── server.go
│   │   ├── tls_test.go
│   │   └── tunnel.go
│   ├── tproxy/
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── getsockopt.go
│   │   ├── getsockopt_i386.go
│   │   ├── server.go
│   │   ├── tcp.go
│   │   ├── tproxy_stub.go
│   │   ├── tunnel.go
│   │   └── udp.go
│   ├── transport/
│   │   ├── client.go
│   │   ├── config.go
│   │   ├── conn.go
│   │   ├── server.go
│   │   ├── transport_test.go
│   │   └── tunnel.go
│   ├── trojan/
│   │   ├── client.go
│   │   ├── config.go
│   │   ├── packet.go
│   │   ├── server.go
│   │   ├── trojan_test.go
│   │   └── tunnel.go
│   ├── tunnel.go
│   └── websocket/
│       ├── client.go
│       ├── config.go
│       ├── conn.go
│       ├── server.go
│       ├── tunnel.go
│       └── websocket_test.go
├── url/
│   ├── option.go
│   ├── option_test.go
│   ├── share_link.go
│   └── share_link_test.go
└── version/
    └── version.go
Download .txt
SYMBOL INDEX (1033 symbols across 138 files)

FILE: api/api.go
  type Handler (line 10) | type Handler
  function RegisterHandler (line 14) | func RegisterHandler(name string, handler Handler) {
  function RunService (line 18) | func RunService(ctx context.Context, name string, auth statistic.Authent...

FILE: api/control/control.go
  type apiController (line 18) | type apiController struct
    method Name (line 36) | func (apiController) Name() string {
    method listUsers (line 40) | func (o *apiController) listUsers(apiClient service.TrojanServerServic...
    method getUsers (line 63) | func (o *apiController) getUsers(apiClient service.TrojanServerService...
    method setUsers (line 88) | func (o *apiController) setUsers(apiClient service.TrojanServerService...
    method Handle (line 136) | func (o *apiController) Handle() error {
    method Priority (line 169) | func (o *apiController) Priority() int {
  function init (line 173) | func init() {

FILE: api/service/api.pb.go
  constant _ (line 18) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
  constant _ (line 20) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
  type SetUsersRequest_Operation (line 23) | type SetUsersRequest_Operation
    method Enum (line 45) | func (x SetUsersRequest_Operation) Enum() *SetUsersRequest_Operation {
    method String (line 51) | func (x SetUsersRequest_Operation) String() string {
    method Descriptor (line 55) | func (SetUsersRequest_Operation) Descriptor() protoreflect.EnumDescrip...
    method Type (line 59) | func (SetUsersRequest_Operation) Type() protoreflect.EnumType {
    method Number (line 63) | func (x SetUsersRequest_Operation) Number() protoreflect.EnumNumber {
    method EnumDescriptor (line 68) | func (SetUsersRequest_Operation) EnumDescriptor() ([]byte, []int) {
  constant SetUsersRequest_Add (line 26) | SetUsersRequest_Add    SetUsersRequest_Operation = 0
  constant SetUsersRequest_Delete (line 27) | SetUsersRequest_Delete SetUsersRequest_Operation = 1
  constant SetUsersRequest_Modify (line 28) | SetUsersRequest_Modify SetUsersRequest_Operation = 2
  type Traffic (line 72) | type Traffic struct
    method Reset (line 81) | func (x *Traffic) Reset() {
    method String (line 90) | func (x *Traffic) String() string {
    method ProtoMessage (line 94) | func (*Traffic) ProtoMessage() {}
    method ProtoReflect (line 96) | func (x *Traffic) ProtoReflect() protoreflect.Message {
    method Descriptor (line 109) | func (*Traffic) Descriptor() ([]byte, []int) {
    method GetUploadTraffic (line 113) | func (x *Traffic) GetUploadTraffic() uint64 {
    method GetDownloadTraffic (line 120) | func (x *Traffic) GetDownloadTraffic() uint64 {
  type Speed (line 127) | type Speed struct
    method Reset (line 136) | func (x *Speed) Reset() {
    method String (line 145) | func (x *Speed) String() string {
    method ProtoMessage (line 149) | func (*Speed) ProtoMessage() {}
    method ProtoReflect (line 151) | func (x *Speed) ProtoReflect() protoreflect.Message {
    method Descriptor (line 164) | func (*Speed) Descriptor() ([]byte, []int) {
    method GetUploadSpeed (line 168) | func (x *Speed) GetUploadSpeed() uint64 {
    method GetDownloadSpeed (line 175) | func (x *Speed) GetDownloadSpeed() uint64 {
  type User (line 182) | type User struct
    method Reset (line 191) | func (x *User) Reset() {
    method String (line 200) | func (x *User) String() string {
    method ProtoMessage (line 204) | func (*User) ProtoMessage() {}
    method ProtoReflect (line 206) | func (x *User) ProtoReflect() protoreflect.Message {
    method Descriptor (line 219) | func (*User) Descriptor() ([]byte, []int) {
    method GetPassword (line 223) | func (x *User) GetPassword() string {
    method GetHash (line 230) | func (x *User) GetHash() string {
  type UserStatus (line 237) | type UserStatus struct
    method Reset (line 250) | func (x *UserStatus) Reset() {
    method String (line 259) | func (x *UserStatus) String() string {
    method ProtoMessage (line 263) | func (*UserStatus) ProtoMessage() {}
    method ProtoReflect (line 265) | func (x *UserStatus) ProtoReflect() protoreflect.Message {
    method Descriptor (line 278) | func (*UserStatus) Descriptor() ([]byte, []int) {
    method GetUser (line 282) | func (x *UserStatus) GetUser() *User {
    method GetTrafficTotal (line 289) | func (x *UserStatus) GetTrafficTotal() *Traffic {
    method GetSpeedCurrent (line 296) | func (x *UserStatus) GetSpeedCurrent() *Speed {
    method GetSpeedLimit (line 303) | func (x *UserStatus) GetSpeedLimit() *Speed {
    method GetIpCurrent (line 310) | func (x *UserStatus) GetIpCurrent() int32 {
    method GetIpLimit (line 317) | func (x *UserStatus) GetIpLimit() int32 {
  type GetTrafficRequest (line 324) | type GetTrafficRequest struct
    method Reset (line 332) | func (x *GetTrafficRequest) Reset() {
    method String (line 341) | func (x *GetTrafficRequest) String() string {
    method ProtoMessage (line 345) | func (*GetTrafficRequest) ProtoMessage() {}
    method ProtoReflect (line 347) | func (x *GetTrafficRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 360) | func (*GetTrafficRequest) Descriptor() ([]byte, []int) {
    method GetUser (line 364) | func (x *GetTrafficRequest) GetUser() *User {
  type GetTrafficResponse (line 371) | type GetTrafficResponse struct
    method Reset (line 382) | func (x *GetTrafficResponse) Reset() {
    method String (line 391) | func (x *GetTrafficResponse) String() string {
    method ProtoMessage (line 395) | func (*GetTrafficResponse) ProtoMessage() {}
    method ProtoReflect (line 397) | func (x *GetTrafficResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 410) | func (*GetTrafficResponse) Descriptor() ([]byte, []int) {
    method GetSuccess (line 414) | func (x *GetTrafficResponse) GetSuccess() bool {
    method GetInfo (line 421) | func (x *GetTrafficResponse) GetInfo() string {
    method GetTrafficTotal (line 428) | func (x *GetTrafficResponse) GetTrafficTotal() *Traffic {
    method GetSpeedCurrent (line 435) | func (x *GetTrafficResponse) GetSpeedCurrent() *Speed {
  type ListUsersRequest (line 442) | type ListUsersRequest struct
    method Reset (line 448) | func (x *ListUsersRequest) Reset() {
    method String (line 457) | func (x *ListUsersRequest) String() string {
    method ProtoMessage (line 461) | func (*ListUsersRequest) ProtoMessage() {}
    method ProtoReflect (line 463) | func (x *ListUsersRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 476) | func (*ListUsersRequest) Descriptor() ([]byte, []int) {
  type ListUsersResponse (line 480) | type ListUsersResponse struct
    method Reset (line 488) | func (x *ListUsersResponse) Reset() {
    method String (line 497) | func (x *ListUsersResponse) String() string {
    method ProtoMessage (line 501) | func (*ListUsersResponse) ProtoMessage() {}
    method ProtoReflect (line 503) | func (x *ListUsersResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 516) | func (*ListUsersResponse) Descriptor() ([]byte, []int) {
    method GetStatus (line 520) | func (x *ListUsersResponse) GetStatus() *UserStatus {
  type GetUsersRequest (line 527) | type GetUsersRequest struct
    method Reset (line 535) | func (x *GetUsersRequest) Reset() {
    method String (line 544) | func (x *GetUsersRequest) String() string {
    method ProtoMessage (line 548) | func (*GetUsersRequest) ProtoMessage() {}
    method ProtoReflect (line 550) | func (x *GetUsersRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 563) | func (*GetUsersRequest) Descriptor() ([]byte, []int) {
    method GetUser (line 567) | func (x *GetUsersRequest) GetUser() *User {
  type GetUsersResponse (line 574) | type GetUsersResponse struct
    method Reset (line 584) | func (x *GetUsersResponse) Reset() {
    method String (line 593) | func (x *GetUsersResponse) String() string {
    method ProtoMessage (line 597) | func (*GetUsersResponse) ProtoMessage() {}
    method ProtoReflect (line 599) | func (x *GetUsersResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 612) | func (*GetUsersResponse) Descriptor() ([]byte, []int) {
    method GetSuccess (line 616) | func (x *GetUsersResponse) GetSuccess() bool {
    method GetInfo (line 623) | func (x *GetUsersResponse) GetInfo() string {
    method GetStatus (line 630) | func (x *GetUsersResponse) GetStatus() *UserStatus {
  type SetUsersRequest (line 637) | type SetUsersRequest struct
    method Reset (line 646) | func (x *SetUsersRequest) Reset() {
    method String (line 655) | func (x *SetUsersRequest) String() string {
    method ProtoMessage (line 659) | func (*SetUsersRequest) ProtoMessage() {}
    method ProtoReflect (line 661) | func (x *SetUsersRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 674) | func (*SetUsersRequest) Descriptor() ([]byte, []int) {
    method GetStatus (line 678) | func (x *SetUsersRequest) GetStatus() *UserStatus {
    method GetOperation (line 685) | func (x *SetUsersRequest) GetOperation() SetUsersRequest_Operation {
  type SetUsersResponse (line 692) | type SetUsersResponse struct
    method Reset (line 701) | func (x *SetUsersResponse) Reset() {
    method String (line 710) | func (x *SetUsersResponse) String() string {
    method ProtoMessage (line 714) | func (*SetUsersResponse) ProtoMessage() {}
    method ProtoReflect (line 716) | func (x *SetUsersResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 729) | func (*SetUsersResponse) Descriptor() ([]byte, []int) {
    method GetSuccess (line 733) | func (x *SetUsersResponse) GetSuccess() bool {
    method GetInfo (line 740) | func (x *SetUsersResponse) GetInfo() string {
  function file_api_proto_rawDescGZIP (line 863) | func file_api_proto_rawDescGZIP() []byte {
  function init (line 915) | func init() { file_api_proto_init() }
  function file_api_proto_init (line 916) | func file_api_proto_init() {

FILE: api/service/api_grpc.pb.go
  constant _ (line 15) | _ = grpc.SupportPackageIsVersion7
  type TrojanClientServiceClient (line 20) | type TrojanClientServiceClient interface
  type trojanClientServiceClient (line 24) | type trojanClientServiceClient struct
    method GetTraffic (line 32) | func (c *trojanClientServiceClient) GetTraffic(ctx context.Context, in...
  function NewTrojanClientServiceClient (line 28) | func NewTrojanClientServiceClient(cc grpc.ClientConnInterface) TrojanCli...
  type TrojanClientServiceServer (line 44) | type TrojanClientServiceServer interface
  type UnimplementedTrojanClientServiceServer (line 50) | type UnimplementedTrojanClientServiceServer struct
    method GetTraffic (line 53) | func (UnimplementedTrojanClientServiceServer) GetTraffic(context.Conte...
    method mustEmbedUnimplementedTrojanClientServiceServer (line 56) | func (UnimplementedTrojanClientServiceServer) mustEmbedUnimplementedTr...
  type UnsafeTrojanClientServiceServer (line 61) | type UnsafeTrojanClientServiceServer interface
  function RegisterTrojanClientServiceServer (line 65) | func RegisterTrojanClientServiceServer(s grpc.ServiceRegistrar, srv Troj...
  function _TrojanClientService_GetTraffic_Handler (line 69) | func _TrojanClientService_GetTraffic_Handler(srv interface{}, ctx contex...
  type TrojanServerServiceClient (line 106) | type TrojanServerServiceClient interface
  type trojanServerServiceClient (line 115) | type trojanServerServiceClient struct
    method ListUsers (line 123) | func (c *trojanServerServiceClient) ListUsers(ctx context.Context, in ...
    method GetUsers (line 155) | func (c *trojanServerServiceClient) GetUsers(ctx context.Context, opts...
    method SetUsers (line 186) | func (c *trojanServerServiceClient) SetUsers(ctx context.Context, opts...
  function NewTrojanServerServiceClient (line 119) | func NewTrojanServerServiceClient(cc grpc.ClientConnInterface) TrojanSer...
  type TrojanServerService_ListUsersClient (line 138) | type TrojanServerService_ListUsersClient interface
  type trojanServerServiceListUsersClient (line 143) | type trojanServerServiceListUsersClient struct
    method Recv (line 147) | func (x *trojanServerServiceListUsersClient) Recv() (*ListUsersRespons...
  type TrojanServerService_GetUsersClient (line 164) | type TrojanServerService_GetUsersClient interface
  type trojanServerServiceGetUsersClient (line 170) | type trojanServerServiceGetUsersClient struct
    method Send (line 174) | func (x *trojanServerServiceGetUsersClient) Send(m *GetUsersRequest) e...
    method Recv (line 178) | func (x *trojanServerServiceGetUsersClient) Recv() (*GetUsersResponse,...
  type TrojanServerService_SetUsersClient (line 195) | type TrojanServerService_SetUsersClient interface
  type trojanServerServiceSetUsersClient (line 201) | type trojanServerServiceSetUsersClient struct
    method Send (line 205) | func (x *trojanServerServiceSetUsersClient) Send(m *SetUsersRequest) e...
    method Recv (line 209) | func (x *trojanServerServiceSetUsersClient) Recv() (*SetUsersResponse,...
  type TrojanServerServiceServer (line 220) | type TrojanServerServiceServer interface
  type UnimplementedTrojanServerServiceServer (line 231) | type UnimplementedTrojanServerServiceServer struct
    method ListUsers (line 234) | func (UnimplementedTrojanServerServiceServer) ListUsers(*ListUsersRequ...
    method GetUsers (line 237) | func (UnimplementedTrojanServerServiceServer) GetUsers(TrojanServerSer...
    method SetUsers (line 240) | func (UnimplementedTrojanServerServiceServer) SetUsers(TrojanServerSer...
    method mustEmbedUnimplementedTrojanServerServiceServer (line 243) | func (UnimplementedTrojanServerServiceServer) mustEmbedUnimplementedTr...
  type UnsafeTrojanServerServiceServer (line 248) | type UnsafeTrojanServerServiceServer interface
  function RegisterTrojanServerServiceServer (line 252) | func RegisterTrojanServerServiceServer(s grpc.ServiceRegistrar, srv Troj...
  function _TrojanServerService_ListUsers_Handler (line 256) | func _TrojanServerService_ListUsers_Handler(srv interface{}, stream grpc...
  type TrojanServerService_ListUsersServer (line 264) | type TrojanServerService_ListUsersServer interface
  type trojanServerServiceListUsersServer (line 269) | type trojanServerServiceListUsersServer struct
    method Send (line 273) | func (x *trojanServerServiceListUsersServer) Send(m *ListUsersResponse...
  function _TrojanServerService_GetUsers_Handler (line 277) | func _TrojanServerService_GetUsers_Handler(srv interface{}, stream grpc....
  type TrojanServerService_GetUsersServer (line 281) | type TrojanServerService_GetUsersServer interface
  type trojanServerServiceGetUsersServer (line 287) | type trojanServerServiceGetUsersServer struct
    method Send (line 291) | func (x *trojanServerServiceGetUsersServer) Send(m *GetUsersResponse) ...
    method Recv (line 295) | func (x *trojanServerServiceGetUsersServer) Recv() (*GetUsersRequest, ...
  function _TrojanServerService_SetUsers_Handler (line 303) | func _TrojanServerService_SetUsers_Handler(srv interface{}, stream grpc....
  type TrojanServerService_SetUsersServer (line 307) | type TrojanServerService_SetUsersServer interface
  type trojanServerServiceSetUsersServer (line 313) | type trojanServerServiceSetUsersServer struct
    method Send (line 317) | func (x *trojanServerServiceSetUsersServer) Send(m *SetUsersResponse) ...
    method Recv (line 321) | func (x *trojanServerServiceSetUsersServer) Recv() (*SetUsersRequest, ...

FILE: api/service/client.go
  type ClientAPI (line 15) | type ClientAPI struct
    method GetTraffic (line 26) | func (s *ClientAPI) GetTraffic(ctx context.Context, req *GetTrafficReq...
  function RunClientAPI (line 54) | func RunClientAPI(ctx context.Context, auth statistic.Authenticator) err...
  function init (line 96) | func init() {

FILE: api/service/client_test.go
  function TestClientAPI (line 16) | func TestClientAPI(t *testing.T) {

FILE: api/service/config.go
  constant Name (line 5) | Name = "API_SERVICE"
  type SSLConfig (line 7) | type SSLConfig struct
  type APIConfig (line 15) | type APIConfig struct
  type Config (line 22) | type Config struct
  function init (line 26) | func init() {

FILE: api/service/server.go
  type ServerAPI (line 22) | type ServerAPI struct
    method GetUsers (line 27) | func (s *ServerAPI) GetUsers(stream TrojanServerService_GetUsersServer...
    method SetUsers (line 82) | func (s *ServerAPI) SetUsers(stream TrojanServerService_SetUsersServer...
    method ListUsers (line 147) | func (s *ServerAPI) ListUsers(req *ListUsersRequest, stream TrojanServ...
  function newAPIServer (line 184) | func newAPIServer(cfg *Config) (*grpc.Server, error) {
  function RunServerAPI (line 218) | func RunServerAPI(ctx context.Context, auth statistic.Authenticator) err...
  function init (line 259) | func init() {

FILE: api/service/server_test.go
  function TestServerAPI (line 20) | func TestServerAPI(t *testing.T) {
  function TestTLSRSA (line 160) | func TestTLSRSA(t *testing.T) {
  function TestTLSECC (line 210) | func TestTLSECC(t *testing.T) {
  function init (line 416) | func init() {

FILE: common/common.go
  type Runnable (line 12) | type Runnable interface
  function SHA224String (line 17) | func SHA224String(password string) string {
  function GetProgramDir (line 28) | func GetProgramDir() string {
  function GetAssetLocation (line 36) | func GetAssetLocation(file string) string {

FILE: common/error.go
  type Error (line 7) | type Error struct
    method Error (line 11) | func (e *Error) Error() string {
    method Base (line 15) | func (e *Error) Base(err error) *Error {
  function NewError (line 22) | func NewError(info string) *Error {
  function Must (line 28) | func Must(err error) {
  function Must2 (line 35) | func Must2(_ interface{}, err error) {

FILE: common/geodata/cache.go
  type geoipCache (line 14) | type geoipCache
    method Has (line 16) | func (g geoipCache) Has(key string) bool {
    method Get (line 20) | func (g geoipCache) Get(key string) *v2router.GeoIP {
    method Set (line 27) | func (g geoipCache) Set(key string, value *v2router.GeoIP) {
    method Unmarshal (line 34) | func (g geoipCache) Unmarshal(filename, code string) (*v2router.GeoIP,...
  type geositeCache (line 80) | type geositeCache
    method Has (line 82) | func (g geositeCache) Has(key string) bool {
    method Get (line 86) | func (g geositeCache) Get(key string) *v2router.GeoSite {
    method Set (line 93) | func (g geositeCache) Set(key string, value *v2router.GeoSite) {
    method Unmarshal (line 100) | func (g geositeCache) Unmarshal(filename, code string) (*v2router.GeoS...

FILE: common/geodata/decode.go
  function EmitBytes (line 29) | func EmitBytes(f io.ReadSeeker, code string) ([]byte, error) {
  function Decode (line 102) | func Decode(filename, code string) ([]byte, error) {

FILE: common/geodata/decode_test.go
  function init (line 16) | func init() {
  function TestDecodeGeoIP (line 45) | func TestDecodeGeoIP(t *testing.T) {
  function TestDecodeGeoSite (line 58) | func TestDecodeGeoSite(t *testing.T) {
  function BenchmarkLoadGeoIP (line 71) | func BenchmarkLoadGeoIP(b *testing.B) {
  function BenchmarkLoadGeoSite (line 88) | func BenchmarkLoadGeoSite(b *testing.B) {

FILE: common/geodata/interface.go
  type GeodataLoader (line 5) | type GeodataLoader interface

FILE: common/geodata/loader.go
  type geodataCache (line 9) | type geodataCache struct
    method LoadIP (line 21) | func (g *geodataCache) LoadIP(filename, country string) ([]*v2router.C...
    method LoadSite (line 30) | func (g *geodataCache) LoadSite(filename, list string) ([]*v2router.Do...
    method LoadGeoIP (line 39) | func (g *geodataCache) LoadGeoIP(country string) ([]*v2router.CIDR, er...
    method LoadGeoSite (line 43) | func (g *geodataCache) LoadGeoSite(list string) ([]*v2router.Domain, e...
  function NewGeodataLoader (line 14) | func NewGeodataLoader() GeodataLoader {

FILE: common/io.go
  type RewindReader (line 11) | type RewindReader struct
    method Read (line 21) | func (r *RewindReader) Read(p []byte) (int, error) {
    method ReadByte (line 43) | func (r *RewindReader) ReadByte() (byte, error) {
    method Discard (line 49) | func (r *RewindReader) Discard(n int) (int, error) {
    method Rewind (line 66) | func (r *RewindReader) Rewind() {
    method StopBuffering (line 76) | func (r *RewindReader) StopBuffering() {
    method SetBufferSize (line 82) | func (r *RewindReader) SetBufferSize(size int) {
  type RewindConn (line 104) | type RewindConn struct
    method Read (line 109) | func (c *RewindConn) Read(p []byte) (int, error) {
  function NewRewindConn (line 113) | func NewRewindConn(conn net.Conn) *RewindConn {
  type StickyWriter (line 122) | type StickyWriter struct
    method Write (line 128) | func (w *StickyWriter) Write(p []byte) (int, error) {

FILE: common/io_test.go
  function TestBufferedReader (line 11) | func TestBufferedReader(t *testing.T) {

FILE: common/net.go
  constant KiB (line 17) | KiB = 1024
  constant MiB (line 18) | MiB = KiB * 1024
  constant GiB (line 19) | GiB = MiB * 1024
  function HumanFriendlyTraffic (line 22) | func HumanFriendlyTraffic(bytes uint64) string {
  function PickPort (line 35) | func PickPort(network string, host string) int {
  function WriteAllBytes (line 69) | func WriteAllBytes(writer io.Writer, payload []byte) error {
  function WriteFile (line 80) | func WriteFile(path string, payload []byte) error {
  function FetchHTTPContent (line 90) | func FetchHTTPContent(target string) ([]byte, error) {

FILE: common/sync.go
  type Notifier (line 4) | type Notifier struct
    method Signal (line 16) | func (n *Notifier) Signal() {
    method Wait (line 24) | func (n *Notifier) Wait() <-chan struct{} {
  function NewNotifier (line 9) | func NewNotifier() *Notifier {

FILE: config/config.go
  type Creator (line 13) | type Creator
  function RegisterConfigCreator (line 16) | func RegisterConfigCreator(name string, creator Creator) {
  function parseJSON (line 21) | func parseJSON(data []byte) (map[string]interface{}, error) {
  function parseYAML (line 33) | func parseYAML(data []byte) (map[string]interface{}, error) {
  function WithJSONConfig (line 45) | func WithJSONConfig(ctx context.Context, data []byte) (context.Context, ...
  function WithYAMLConfig (line 58) | func WithYAMLConfig(ctx context.Context, data []byte) (context.Context, ...
  function WithConfig (line 71) | func WithConfig(ctx context.Context, name string, cfg interface{}) conte...
  function FromContext (line 77) | func FromContext(ctx context.Context, name string) interface{} {

FILE: config/config_test.go
  type Foo (line 10) | type Foo struct
  type TestStruct (line 15) | type TestStruct struct
  function creator (line 21) | func creator() interface{} {
  function TestJSONConfig (line 25) | func TestJSONConfig(t *testing.T) {
  function TestYAMLConfig (line 47) | func TestYAMLConfig(t *testing.T) {

FILE: easy/easy.go
  type easy (line 15) | type easy struct
    method Name (line 50) | func (o *easy) Name() string {
    method Handle (line 54) | func (o *easy) Handle() error {
    method Priority (line 158) | func (o *easy) Priority() int {
  type ClientConfig (line 25) | type ClientConfig struct
  type TLS (line 34) | type TLS struct
  type ServerConfig (line 40) | type ServerConfig struct
  function init (line 162) | func init() {

FILE: log/golog/buffer/buffer.go
  type Buffer (line 7) | type Buffer
    method Reset (line 10) | func (b *Buffer) Reset() {
    method Append (line 15) | func (b *Buffer) Append(data []byte) {
    method AppendByte (line 20) | func (b *Buffer) AppendByte(data byte) {
    method AppendInt (line 25) | func (b *Buffer) AppendInt(val int, width int) {
    method Bytes (line 40) | func (b Buffer) Bytes() []byte {

FILE: log/golog/buffer/buffer_test.go
  function TestBufferAllocation (line 14) | func TestBufferAllocation(t *testing.T) {
  function TestBufferReset (line 52) | func TestBufferReset(t *testing.T) {

FILE: log/golog/colorful/colorful.go
  type ColorBuffer (line 13) | type ColorBuffer struct
    method Off (line 43) | func (cb *ColorBuffer) Off() {
    method Red (line 48) | func (cb *ColorBuffer) Red() {
    method Green (line 53) | func (cb *ColorBuffer) Green() {
    method Orange (line 58) | func (cb *ColorBuffer) Orange() {
    method Blue (line 63) | func (cb *ColorBuffer) Blue() {
    method Purple (line 68) | func (cb *ColorBuffer) Purple() {
    method Cyan (line 73) | func (cb *ColorBuffer) Cyan() {
    method Gray (line 78) | func (cb *ColorBuffer) Gray() {
  function init (line 29) | func init() {
  function mixer (line 83) | func mixer(data []byte, color []byte) []byte {
  function Red (line 89) | func Red(data []byte) []byte {
  function Green (line 94) | func Green(data []byte) []byte {
  function Orange (line 99) | func Orange(data []byte) []byte {
  function Blue (line 104) | func Blue(data []byte) []byte {
  function Purple (line 109) | func Purple(data []byte) []byte {
  function Cyan (line 114) | func Cyan(data []byte) []byte {
  function Gray (line 119) | func Gray(data []byte) []byte {

FILE: log/golog/colorful/colorful_test.go
  function TestColorBuffer (line 16) | func TestColorBuffer(t *testing.T) {
  function TestColorMixer (line 48) | func TestColorMixer(t *testing.T) {

FILE: log/golog/golog.go
  function init (line 22) | func init() {
  type FdWriter (line 28) | type FdWriter interface
  type Logger (line 34) | type Logger struct
    method SetLogLevel (line 111) | func (l *Logger) SetLogLevel(level log.LogLevel) {
    method SetOutput (line 117) | func (l *Logger) SetOutput(w io.Writer) {
    method WithColor (line 128) | func (l *Logger) WithColor() *Logger {
    method WithoutColor (line 136) | func (l *Logger) WithoutColor() *Logger {
    method WithDebug (line 144) | func (l *Logger) WithDebug() *Logger {
    method WithoutDebug (line 152) | func (l *Logger) WithoutDebug() *Logger {
    method IsDebug (line 160) | func (l *Logger) IsDebug() bool {
    method WithTimestamp (line 167) | func (l *Logger) WithTimestamp() *Logger {
    method WithoutTimestamp (line 175) | func (l *Logger) WithoutTimestamp() *Logger {
    method Quiet (line 183) | func (l *Logger) Quiet() *Logger {
    method NoQuiet (line 191) | func (l *Logger) NoQuiet() *Logger {
    method IsQuiet (line 199) | func (l *Logger) IsQuiet() bool {
    method Output (line 206) | func (l *Logger) Output(depth int, prefix Prefix, data string) error {
    method Fatal (line 298) | func (l *Logger) Fatal(v ...interface{}) {
    method Fatalf (line 307) | func (l *Logger) Fatalf(format string, v ...interface{}) {
    method Error (line 315) | func (l *Logger) Error(v ...interface{}) {
    method Errorf (line 322) | func (l *Logger) Errorf(format string, v ...interface{}) {
    method Warn (line 329) | func (l *Logger) Warn(v ...interface{}) {
    method Warnf (line 336) | func (l *Logger) Warnf(format string, v ...interface{}) {
    method Info (line 343) | func (l *Logger) Info(v ...interface{}) {
    method Infof (line 350) | func (l *Logger) Infof(format string, v ...interface{}) {
    method Debug (line 357) | func (l *Logger) Debug(v ...interface{}) {
    method Debugf (line 364) | func (l *Logger) Debugf(format string, v ...interface{}) {
    method Trace (line 371) | func (l *Logger) Trace(v ...interface{}) {
    method Tracef (line 378) | func (l *Logger) Tracef(format string, v ...interface{}) {
  type Prefix (line 46) | type Prefix struct
  function New (line 103) | func New(out FdWriter) *Logger {

FILE: log/log.go
  type LogLevel (line 10) | type LogLevel
  constant AllLevel (line 13) | AllLevel   LogLevel = 0
  constant InfoLevel (line 14) | InfoLevel  LogLevel = 1
  constant WarnLevel (line 15) | WarnLevel  LogLevel = 2
  constant ErrorLevel (line 16) | ErrorLevel LogLevel = 3
  constant FatalLevel (line 17) | FatalLevel LogLevel = 4
  constant OffLevel (line 18) | OffLevel   LogLevel = 5
  type Logger (line 21) | type Logger interface
  type EmptyLogger (line 40) | type EmptyLogger struct
    method SetLogLevel (line 42) | func (l *EmptyLogger) SetLogLevel(LogLevel) {}
    method Fatal (line 44) | func (l *EmptyLogger) Fatal(v ...interface{}) { os.Exit(1) }
    method Fatalf (line 46) | func (l *EmptyLogger) Fatalf(format string, v ...interface{}) { os.Exi...
    method Error (line 48) | func (l *EmptyLogger) Error(v ...interface{}) {}
    method Errorf (line 50) | func (l *EmptyLogger) Errorf(format string, v ...interface{}) {}
    method Warn (line 52) | func (l *EmptyLogger) Warn(v ...interface{}) {}
    method Warnf (line 54) | func (l *EmptyLogger) Warnf(format string, v ...interface{}) {}
    method Info (line 56) | func (l *EmptyLogger) Info(v ...interface{}) {}
    method Infof (line 58) | func (l *EmptyLogger) Infof(format string, v ...interface{}) {}
    method Debug (line 60) | func (l *EmptyLogger) Debug(v ...interface{}) {}
    method Debugf (line 62) | func (l *EmptyLogger) Debugf(format string, v ...interface{}) {}
    method Trace (line 64) | func (l *EmptyLogger) Trace(v ...interface{}) {}
    method Tracef (line 66) | func (l *EmptyLogger) Tracef(format string, v ...interface{}) {}
    method SetOutput (line 68) | func (l *EmptyLogger) SetOutput(w io.Writer) {}
  function Error (line 70) | func Error(v ...interface{}) {
  function Errorf (line 74) | func Errorf(format string, v ...interface{}) {
  function Warn (line 78) | func Warn(v ...interface{}) {
  function Warnf (line 82) | func Warnf(format string, v ...interface{}) {
  function Info (line 86) | func Info(v ...interface{}) {
  function Infof (line 90) | func Infof(format string, v ...interface{}) {
  function Debug (line 94) | func Debug(v ...interface{}) {
  function Debugf (line 98) | func Debugf(format string, v ...interface{}) {
  function Trace (line 102) | func Trace(v ...interface{}) {
  function Tracef (line 106) | func Tracef(format string, v ...interface{}) {
  function Fatal (line 110) | func Fatal(v ...interface{}) {
  function Fatalf (line 114) | func Fatalf(format string, v ...interface{}) {
  function SetLogLevel (line 118) | func SetLogLevel(level LogLevel) {
  function SetOutput (line 122) | func SetOutput(w io.Writer) {
  function RegisterLogger (line 126) | func RegisterLogger(l Logger) {

FILE: log/simplelog/simplelog.go
  function init (line 11) | func init() {
  type SimpleLogger (line 15) | type SimpleLogger struct
    method SetLogLevel (line 19) | func (l *SimpleLogger) SetLogLevel(level log.LogLevel) {
    method Fatal (line 23) | func (l *SimpleLogger) Fatal(v ...interface{}) {
    method Fatalf (line 30) | func (l *SimpleLogger) Fatalf(format string, v ...interface{}) {
    method Error (line 37) | func (l *SimpleLogger) Error(v ...interface{}) {
    method Errorf (line 43) | func (l *SimpleLogger) Errorf(format string, v ...interface{}) {
    method Warn (line 49) | func (l *SimpleLogger) Warn(v ...interface{}) {
    method Warnf (line 55) | func (l *SimpleLogger) Warnf(format string, v ...interface{}) {
    method Info (line 61) | func (l *SimpleLogger) Info(v ...interface{}) {
    method Infof (line 67) | func (l *SimpleLogger) Infof(format string, v ...interface{}) {
    method Debug (line 73) | func (l *SimpleLogger) Debug(v ...interface{}) {
    method Debugf (line 79) | func (l *SimpleLogger) Debugf(format string, v ...interface{}) {
    method Trace (line 85) | func (l *SimpleLogger) Trace(v ...interface{}) {
    method Tracef (line 91) | func (l *SimpleLogger) Tracef(format string, v ...interface{}) {
    method SetOutput (line 97) | func (l *SimpleLogger) SetOutput(io.Writer) {

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

FILE: option/option.go
  type Handler (line 5) | type Handler interface
  function RegisterHandler (line 13) | func RegisterHandler(h Handler) {
  function PopOptionHandler (line 17) | func PopOptionHandler() (Handler, error) {

FILE: proxy/client/client.go
  constant Name (line 21) | Name = "CLIENT"
  function GenerateClientTree (line 24) | func GenerateClientTree(transportPlugin bool, muxEnabled bool, wsEnabled...
  function init (line 45) | func init() {

FILE: proxy/client/config.go
  type MuxConfig (line 5) | type MuxConfig struct
  type WebsocketConfig (line 9) | type WebsocketConfig struct
  type RouterConfig (line 13) | type RouterConfig struct
  type ShadowsocksConfig (line 17) | type ShadowsocksConfig struct
  type TransportPluginConfig (line 21) | type TransportPluginConfig struct
  type Config (line 25) | type Config struct
  function init (line 33) | func init() {

FILE: proxy/config.go
  type Config (line 5) | type Config struct
  function init (line 11) | func init() {

FILE: proxy/custom/config.go
  constant Name (line 5) | Name = "CUSTOM"
  type NodeConfig (line 7) | type NodeConfig struct
  type StackConfig (line 13) | type StackConfig struct
  type Config (line 18) | type Config struct
  function init (line 23) | func init() {

FILE: proxy/custom/custom.go
  function convert (line 15) | func convert(i interface{}) interface{} {
  function buildNodes (line 31) | func buildNodes(ctx context.Context, nodeConfigList []NodeConfig) (map[s...
  function init (line 54) | func init() {

FILE: proxy/forward/forward.go
  constant Name (line 13) | Name = "FORWARD"
  function init (line 15) | func init() {
  function init (line 35) | func init() {

FILE: proxy/nat/nat.go
  constant Name (line 17) | Name = "NAT"
  function init (line 19) | func init() {
  function init (line 42) | func init() {

FILE: proxy/option.go
  type Option (line 18) | type Option struct
    method Name (line 22) | func (o *Option) Name() string {
    method Handle (line 44) | func (o *Option) Handle() error {
    method Priority (line 90) | func (o *Option) Priority() int {
  function detectAndReadConfig (line 26) | func detectAndReadConfig(file string) ([]byte, bool, error) {
  function init (line 94) | func init() {
  type StdinOption (line 104) | type StdinOption struct
    method Name (line 109) | func (o *StdinOption) Name() string {
    method Handle (line 113) | func (o *StdinOption) Handle() error {
    method Priority (line 145) | func (o *StdinOption) Priority() int {
    method isFormatJson (line 149) | func (o *StdinOption) isFormatJson() (isJson bool, e error) {

FILE: proxy/proxy.go
  constant Name (line 17) | Name = "PROXY"
  constant MaxPacketSize (line 20) | MaxPacketSize = 1024 * 8
  type Proxy (line 24) | type Proxy struct
    method Run (line 31) | func (p *Proxy) Run() error {
    method Close (line 38) | func (p *Proxy) Close() error {
    method relayConnLoop (line 47) | func (p *Proxy) relayConnLoop() {
    method relayPacketLoop (line 93) | func (p *Proxy) relayPacketLoop() {
  function NewProxy (line 153) | func NewProxy(ctx context.Context, cancel context.CancelFunc, sources []...
  type Creator (line 162) | type Creator
  function RegisterProxyCreator (line 166) | func RegisterProxyCreator(name string, creator Creator) {
  function NewProxyFromConfigData (line 170) | func NewProxyFromConfigData(data []byte, isJSON bool) (*Proxy, error) {

FILE: proxy/server/config.go
  function init (line 8) | func init() {

FILE: proxy/server/server.go
  constant Name (line 20) | Name = "SERVER"
  function init (line 22) | func init() {

FILE: proxy/stack.go
  type Node (line 10) | type Node struct
    method BuildNext (line 19) | func (n *Node) BuildNext(name string) *Node {
    method LinkNextNode (line 41) | func (n *Node) LinkNextNode(next *Node) *Node {
  function FindAllEndpoints (line 58) | func FindAllEndpoints(root *Node) []tunnel.Server {
  function CreateClientStack (line 70) | func CreateClientStack(ctx context.Context, clientStack []string) (tunne...
  function CreateServerStack (line 86) | func CreateServerStack(ctx context.Context, serverStack []string) (tunne...

FILE: redirector/redirector.go
  type Dial (line 13) | type Dial
  function defaultDial (line 15) | func defaultDial(addr net.Addr) (net.Conn, error) {
  type Redirection (line 19) | type Redirection struct
  type Redirector (line 25) | type Redirector struct
    method Redirect (line 30) | func (r *Redirector) Redirect(redirection *Redirection) {
    method worker (line 39) | func (r *Redirector) worker() {
  function NewRedirector (line 89) | func NewRedirector(ctx context.Context) *Redirector {

FILE: redirector/redirector_test.go
  function TestRedirector (line 16) | func TestRedirector(t *testing.T) {

FILE: statistic/memory/config.go
  type Config (line 7) | type Config struct
  function init (line 11) | func init() {

FILE: statistic/memory/memory.go
  constant Name (line 17) | Name = "MEMORY"
  type User (line 19) | type User struct
    method Close (line 43) | func (u *User) Close() error {
    method AddIP (line 49) | func (u *User) AddIP(ip string) bool {
    method DelIP (line 65) | func (u *User) DelIP(ip string) bool {
    method GetIP (line 78) | func (u *User) GetIP() int {
    method SetIPLimit (line 82) | func (u *User) SetIPLimit(n int) {
    method GetIPLimit (line 86) | func (u *User) GetIPLimit() int {
    method AddTraffic (line 90) | func (u *User) AddTraffic(sent, recv int) {
    method SetSpeedLimit (line 103) | func (u *User) SetSpeedLimit(send, recv int) {
    method GetSpeedLimit (line 119) | func (u *User) GetSpeedLimit() (send, recv int) {
    method Hash (line 132) | func (u *User) Hash() string {
    method SetTraffic (line 136) | func (u *User) SetTraffic(send, recv uint64) {
    method GetTraffic (line 141) | func (u *User) GetTraffic() (uint64, uint64) {
    method ResetTraffic (line 145) | func (u *User) ResetTraffic() (uint64, uint64) {
    method speedUpdater (line 153) | func (u *User) speedUpdater() {
    method GetSpeed (line 169) | func (u *User) GetSpeed() (uint64, uint64) {
  type Authenticator (line 173) | type Authenticator struct
    method AuthUser (line 178) | func (a *Authenticator) AuthUser(hash string) (bool, statistic.User) {
    method AddUser (line 185) | func (a *Authenticator) AddUser(hash string) error {
    method DelUser (line 200) | func (a *Authenticator) DelUser(hash string) error {
    method ListUsers (line 210) | func (a *Authenticator) ListUsers() []statistic.User {
    method Close (line 219) | func (a *Authenticator) Close() error {
  function NewAuthenticator (line 223) | func NewAuthenticator(ctx context.Context) (statistic.Authenticator, err...
  function init (line 236) | func init() {

FILE: statistic/memory/memory_test.go
  function TestMemoryAuth (line 14) | func TestMemoryAuth(t *testing.T) {
  function BenchmarkMemoryUsage (line 124) | func BenchmarkMemoryUsage(b *testing.B) {

FILE: statistic/mysql/config.go
  type MySQLConfig (line 7) | type MySQLConfig struct
  type Config (line 17) | type Config struct
  function init (line 21) | func init() {

FILE: statistic/mysql/mysql.go
  constant Name (line 20) | Name = "MYSQL"
  type Authenticator (line 22) | type Authenticator struct
    method updater (line 29) | func (a *Authenticator) updater() {
  function connectDatabase (line 80) | func connectDatabase(driverName, username, password, ip string, port int...
  function NewAuthenticator (line 85) | func NewAuthenticator(ctx context.Context) (statistic.Authenticator, err...
  function init (line 113) | func init() {

FILE: statistic/statistics.go
  type TrafficMeter (line 13) | type TrafficMeter interface
  type IPRecorder (line 25) | type IPRecorder interface
  type User (line 33) | type User interface
  type Authenticator (line 38) | type Authenticator interface
  type Creator (line 46) | type Creator
  function RegisterAuthenticatorCreator (line 54) | func RegisterAuthenticatorCreator(name string, creator Creator) {
  function NewAuthenticator (line 58) | func NewAuthenticator(ctx context.Context, name string) (Authenticator, ...

FILE: test/scenario/custom_test.go
  function TestCustom1 (line 12) | func TestCustom1(t *testing.T) {
  function TestCustom2 (line 127) | func TestCustom2(t *testing.T) {

FILE: test/scenario/proxy_test.go
  function init (line 83) | func init() {
  function CheckClientServer (line 88) | func CheckClientServer(clientData, serverData string, socksPort int) (ok...
  function TestClientServerWebsocketSubTree (line 130) | func TestClientServerWebsocketSubTree(t *testing.T) {
  function TestClientServerTrojanSubTree (line 185) | func TestClientServerTrojanSubTree(t *testing.T) {
  function TestWebsocketDetection (line 232) | func TestWebsocketDetection(t *testing.T) {
  function TestPluginWebsocket (line 284) | func TestPluginWebsocket(t *testing.T) {
  function TestForward (line 337) | func TestForward(t *testing.T) {
  function TestLeak (line 429) | func TestLeak(t *testing.T) {
  function SingleThreadBenchmark (line 464) | func SingleThreadBenchmark(clientData, serverData string, socksPort int) {
  function BenchmarkClientServer (line 500) | func BenchmarkClientServer(b *testing.B) {

FILE: test/util/target.go
  function runHelloHTTPServer (line 24) | func runHelloHTTPServer() {
  function runTCPEchoServer (line 61) | func runTCPEchoServer() {
  function runUDPEchoServer (line 92) | func runUDPEchoServer() {
  function GeneratePayload (line 109) | func GeneratePayload(length int) []byte {
  function runTCPBlackHoleServer (line 120) | func runTCPBlackHoleServer() {
  function runUDPBlackHoleServer (line 139) | func runUDPBlackHoleServer() {
  function init (line 157) | func init() {

FILE: test/util/util.go
  function CheckConn (line 14) | func CheckConn(a net.Conn, b net.Conn) bool {
  function CheckPacketOverConn (line 45) | func CheckPacketOverConn(a, b net.PacketConn) bool {
  function CheckPacket (line 78) | func CheckPacket(a, b net.PacketConn) bool {
  function GetTestAddr (line 101) | func GetTestAddr() string {

FILE: tunnel/adapter/config.go
  type Config (line 5) | type Config struct
  function init (line 10) | func init() {

FILE: tunnel/adapter/server.go
  type Server (line 17) | type Server struct
    method acceptConnLoop (line 28) | func (s *Server) acceptConnLoop() {
    method AcceptConn (line 67) | func (s *Server) AcceptConn(overlay tunnel.Tunnel) (tunnel.Conn, error) {
    method AcceptPacket (line 90) | func (s *Server) AcceptPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
    method Close (line 96) | func (s *Server) Close() error {
  function NewServer (line 102) | func NewServer(ctx context.Context, _ tunnel.Server) (*Server, error) {

FILE: tunnel/adapter/tunnel.go
  constant Name (line 9) | Name = "ADAPTER"
  type Tunnel (line 11) | type Tunnel struct
    method Name (line 13) | func (t *Tunnel) Name() string {
    method NewClient (line 17) | func (t *Tunnel) NewClient(ctx context.Context, client tunnel.Client) ...
    method NewServer (line 21) | func (t *Tunnel) NewServer(ctx context.Context, server tunnel.Server) ...
  function init (line 25) | func init() {

FILE: tunnel/dokodemo/config.go
  type Config (line 5) | type Config struct
  function init (line 13) | func init() {

FILE: tunnel/dokodemo/conn.go
  constant MaxPacketSize (line 11) | MaxPacketSize = 1024 * 8
  type Conn (line 13) | type Conn struct
    method Metadata (line 19) | func (c *Conn) Metadata() *tunnel.Metadata {
  type PacketConn (line 24) | type PacketConn struct
    method Close (line 34) | func (c *PacketConn) Close() error {
    method ReadFrom (line 40) | func (c *PacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
    method WriteTo (line 44) | func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (int, error) {
    method ReadWithMetadata (line 54) | func (c *PacketConn) ReadWithMetadata(p []byte) (int, *tunnel.Metadata...
    method WriteWithMetadata (line 64) | func (c *PacketConn) WriteWithMetadata(p []byte, m *tunnel.Metadata) (...

FILE: tunnel/dokodemo/dokodemo_test.go
  function TestDokodemo (line 15) | func TestDokodemo(t *testing.T) {

FILE: tunnel/dokodemo/server.go
  type Server (line 15) | type Server struct
    method dispatchLoop (line 28) | func (s *Server) dispatchLoop() {
    method AcceptConn (line 91) | func (s *Server) AcceptConn(tunnel.Tunnel) (tunnel.Conn, error) {
    method AcceptPacket (line 104) | func (s *Server) AcceptPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
    method Close (line 113) | func (s *Server) Close() error {
  function NewServer (line 120) | func NewServer(ctx context.Context, _ tunnel.Server) (*Server, error) {

FILE: tunnel/dokodemo/tunnel.go
  constant Name (line 10) | Name = "DOKODEMO"
  type Tunnel (line 12) | type Tunnel struct
    method Name (line 14) | func (*Tunnel) Name() string {
    method NewServer (line 18) | func (*Tunnel) NewServer(ctx context.Context, underlay tunnel.Server) ...
    method NewClient (line 22) | func (*Tunnel) NewClient(ctx context.Context, underlay tunnel.Client) ...
  function init (line 26) | func init() {

FILE: tunnel/freedom/client.go
  type Client (line 15) | type Client struct
    method DialConn (line 27) | func (c *Client) DialConn(addr *tunnel.Address, _ tunnel.Tunnel) (tunn...
    method DialPacket (line 66) | func (c *Client) DialPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
    method Close (line 107) | func (c *Client) Close() error {
  function NewClient (line 112) | func NewClient(ctx context.Context, _ tunnel.Client) (*Client, error) {

FILE: tunnel/freedom/config.go
  type Config (line 5) | type Config struct
  type TCPConfig (line 12) | type TCPConfig struct
  type ForwardProxyConfig (line 18) | type ForwardProxyConfig struct
  function init (line 26) | func init() {

FILE: tunnel/freedom/conn.go
  constant MaxPacketSize (line 14) | MaxPacketSize = 1024 * 8
  type Conn (line 16) | type Conn struct
    method Metadata (line 20) | func (c *Conn) Metadata() *tunnel.Metadata {
  type PacketConn (line 24) | type PacketConn struct
    method WriteWithMetadata (line 28) | func (c *PacketConn) WriteWithMetadata(p []byte, m *tunnel.Metadata) (...
    method ReadWithMetadata (line 32) | func (c *PacketConn) ReadWithMetadata(p []byte) (int, *tunnel.Metadata...
    method WriteTo (line 45) | func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (int, error) {
  type SocksPacketConn (line 60) | type SocksPacketConn struct
    method WriteWithMetadata (line 66) | func (c *SocksPacketConn) WriteWithMetadata(payload []byte, metadata *...
    method ReadWithMetadata (line 79) | func (c *SocksPacketConn) ReadWithMetadata(payload []byte) (int, *tunn...
    method Close (line 100) | func (c *SocksPacketConn) Close() error {

FILE: tunnel/freedom/freedom_test.go
  function TestConn (line 17) | func TestConn(t *testing.T) {
  function TestPacket (line 40) | func TestPacket(t *testing.T) {
  function TestSocks (line 63) | func TestSocks(t *testing.T) {

FILE: tunnel/freedom/tunnel.go
  constant Name (line 9) | Name = "FREEDOM"
  type Tunnel (line 11) | type Tunnel struct
    method Name (line 13) | func (*Tunnel) Name() string {
    method NewClient (line 17) | func (*Tunnel) NewClient(ctx context.Context, client tunnel.Client) (t...
    method NewServer (line 21) | func (*Tunnel) NewServer(ctx context.Context, client tunnel.Server) (t...
  function init (line 25) | func init() {

FILE: tunnel/http/http_test.go
  function TestHTTP (line 19) | func TestHTTP(t *testing.T) {

FILE: tunnel/http/server.go
  type ConnectConn (line 18) | type ConnectConn struct
    method Metadata (line 23) | func (c *ConnectConn) Metadata() *tunnel.Metadata {
  type OtherConn (line 27) | type OtherConn struct
    method Metadata (line 36) | func (c *OtherConn) Metadata() *tunnel.Metadata {
    method Read (line 40) | func (c *OtherConn) Read(p []byte) (int, error) {
    method Write (line 53) | func (c *OtherConn) Write(p []byte) (int, error) {
    method Close (line 57) | func (c *OtherConn) Close() error {
  type Server (line 64) | type Server struct
    method acceptLoop (line 71) | func (s *Server) acceptLoop() {
    method AcceptConn (line 169) | func (s *Server) AcceptConn(tunnel.Tunnel) (tunnel.Conn, error) {
    method AcceptPacket (line 178) | func (s *Server) AcceptPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
    method Close (line 183) | func (s *Server) Close() error {
  function NewServer (line 188) | func NewServer(ctx context.Context, underlay tunnel.Server) (*Server, er...

FILE: tunnel/http/tunnel.go
  constant Name (line 9) | Name = "HTTP"
  type Tunnel (line 11) | type Tunnel struct
    method Name (line 13) | func (t *Tunnel) Name() string {
    method NewClient (line 17) | func (t *Tunnel) NewClient(ctx context.Context, client tunnel.Client) ...
    method NewServer (line 21) | func (t *Tunnel) NewServer(ctx context.Context, server tunnel.Server) ...
  function init (line 25) | func init() {

FILE: tunnel/metadata.go
  type Command (line 14) | type Command
  type Metadata (line 16) | type Metadata struct
    method ReadFrom (line 21) | func (r *Metadata) ReadFrom(rr io.Reader) error {
    method WriteTo (line 36) | func (r *Metadata) WriteTo(w io.Writer) error {
    method Network (line 48) | func (r *Metadata) Network() string {
    method String (line 52) | func (r *Metadata) String() string {
  type AddressType (line 56) | type AddressType
  constant IPv4 (line 59) | IPv4       AddressType = 1
  constant DomainName (line 60) | DomainName AddressType = 3
  constant IPv6 (line 61) | IPv6       AddressType = 4
  type Address (line 64) | type Address struct
    method String (line 72) | func (a *Address) String() string {
    method Network (line 85) | func (a *Address) Network() string {
    method ResolveIP (line 89) | func (a *Address) ResolveIP() (net.IP, error) {
    method ReadFrom (line 139) | func (a *Address) ReadFrom(r io.Reader) error {
    method WriteTo (line 193) | func (a *Address) WriteTo(w io.Writer) error {
  function NewAddressFromAddr (line 104) | func NewAddressFromAddr(network string, addr string) (*Address, error) {
  function NewAddressFromHostPort (line 114) | func NewAddressFromHostPort(network string, host string, port int) *Addr...

FILE: tunnel/mux/client.go
  type muxID (line 18) | type muxID
  function generateMuxID (line 20) | func generateMuxID() muxID {
  type smuxClientInfo (line 24) | type smuxClientInfo struct
  type Client (line 32) | type Client struct
    method Close (line 42) | func (c *Client) Close() error {
    method cleanLoop (line 53) | func (c *Client) cleanLoop() {
    method newMuxClient (line 99) | func (c *Client) newMuxClient() (*smuxClientInfo, error) {
    method DialConn (line 129) | func (c *Client) DialConn(*tunnel.Address, tunnel.Tunnel) (tunnel.Conn...
    method DialPacket (line 165) | func (c *Client) DialPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
  function NewClient (line 169) | func NewClient(ctx context.Context, underlay tunnel.Client) (*Client, er...

FILE: tunnel/mux/config.go
  type MuxConfig (line 5) | type MuxConfig struct
  type Config (line 11) | type Config struct
  function init (line 15) | func init() {

FILE: tunnel/mux/conn.go
  type stickyConn (line 11) | type stickyConn struct
    method stickToPayload (line 17) | func (c *stickyConn) stickToPayload(p []byte) []byte {
    method Close (line 41) | func (c *stickyConn) Close() error {
    method Write (line 49) | func (c *stickyConn) Write(p []byte) (int, error) {
  function newStickyConn (line 76) | func newStickyConn(conn tunnel.Conn) *stickyConn {
  type Conn (line 84) | type Conn struct
    method Read (line 89) | func (c *Conn) Read(p []byte) (int, error) {
    method Write (line 93) | func (c *Conn) Write(p []byte) (int, error) {
    method Close (line 97) | func (c *Conn) Close() error {

FILE: tunnel/mux/mux_test.go
  function TestMux (line 14) | func TestMux(t *testing.T) {

FILE: tunnel/mux/server.go
  type Server (line 14) | type Server struct
    method acceptConnWorker (line 21) | func (s *Server) acceptConnWorker() {
    method AcceptConn (line 65) | func (s *Server) AcceptConn(tunnel.Tunnel) (tunnel.Conn, error) {
    method AcceptPacket (line 74) | func (s *Server) AcceptPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
    method Close (line 78) | func (s *Server) Close() error {
  function NewServer (line 83) | func NewServer(ctx context.Context, underlay tunnel.Server) (*Server, er...

FILE: tunnel/mux/tunnel.go
  constant Name (line 9) | Name = "MUX"
  type Tunnel (line 11) | type Tunnel struct
    method Name (line 13) | func (*Tunnel) Name() string {
    method NewClient (line 17) | func (*Tunnel) NewClient(ctx context.Context, client tunnel.Client) (t...
    method NewServer (line 21) | func (*Tunnel) NewServer(ctx context.Context, server tunnel.Server) (t...
  function init (line 25) | func init() {

FILE: tunnel/router/client.go
  constant Block (line 23) | Block  = 0
  constant Bypass (line 24) | Bypass = 1
  constant Proxy (line 25) | Proxy  = 2
  constant AsIs (line 29) | AsIs         = 0
  constant IPIfNonMatch (line 30) | IPIfNonMatch = 1
  constant IPOnDemand (line 31) | IPOnDemand   = 2
  constant MaxPacketSize (line 34) | MaxPacketSize = 1024 * 8
  function matchDomain (line 36) | func matchDomain(list []*v2router.Domain, target string) bool {
  function matchIP (line 77) | func matchIP(list []*v2router.CIDR, target net.IP) bool {
  function newIPAddress (line 105) | func newIPAddress(address *tunnel.Address) (*tunnel.Address, error) {
  type Client (line 122) | type Client struct
    method Route (line 133) | func (c *Client) Route(address *tunnel.Address) int {
    method DialConn (line 170) | func (c *Client) DialConn(address *tunnel.Address, overlay tunnel.Tunn...
    method DialPacket (line 189) | func (c *Client) DialPacket(overlay tunnel.Tunnel) (tunnel.PacketConn,...
    method Close (line 211) | func (c *Client) Close() error {
  type codeInfo (line 216) | type codeInfo struct
  function loadCode (line 221) | func loadCode(cfg *Config, prefix string) []codeInfo {
  function NewClient (line 262) | func NewClient(ctx context.Context, underlay tunnel.Client) (*Client, er...

FILE: tunnel/router/config.go
  type Config (line 8) | type Config struct
  type RouterConfig (line 12) | type RouterConfig struct
  function init (line 23) | func init() {

FILE: tunnel/router/conn.go
  type packetInfo (line 13) | type packetInfo struct
  type PacketConn (line 18) | type PacketConn struct
    method packetLoop (line 27) | func (c *PacketConn) packetLoop() {
    method Close (line 69) | func (c *PacketConn) Close() error {
    method ReadFrom (line 75) | func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err err...
    method WriteTo (line 79) | func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro...
    method WriteWithMetadata (line 83) | func (c *PacketConn) WriteWithMetadata(p []byte, m *tunnel.Metadata) (...
    method ReadWithMetadata (line 104) | func (c *PacketConn) ReadWithMetadata(p []byte) (int, *tunnel.Metadata...

FILE: tunnel/router/router_test.go
  type MockClient (line 17) | type MockClient struct
    method DialConn (line 19) | func (m *MockClient) DialConn(address *tunnel.Address, t tunnel.Tunnel...
    method DialPacket (line 23) | func (m *MockClient) DialPacket(t tunnel.Tunnel) (tunnel.PacketConn, e...
    method Close (line 27) | func (m MockClient) Close() error {
  type MockPacketConn (line 31) | type MockPacketConn struct
    method ReadFrom (line 33) | func (m MockPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err ...
    method WriteTo (line 37) | func (m MockPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err e...
    method Close (line 41) | func (m MockPacketConn) Close() error {
    method LocalAddr (line 45) | func (m MockPacketConn) LocalAddr() net.Addr {
    method SetDeadline (line 49) | func (m MockPacketConn) SetDeadline(t time.Time) error {
    method SetReadDeadline (line 53) | func (m MockPacketConn) SetReadDeadline(t time.Time) error {
    method SetWriteDeadline (line 57) | func (m MockPacketConn) SetWriteDeadline(t time.Time) error {
    method WriteWithMetadata (line 61) | func (m MockPacketConn) WriteWithMetadata(bytes []byte, metadata *tunn...
    method ReadWithMetadata (line 65) | func (m MockPacketConn) ReadWithMetadata(bytes []byte) (int, *tunnel.M...
  function TestRouter (line 69) | func TestRouter(t *testing.T) {

FILE: tunnel/router/tunnel.go
  constant Name (line 9) | Name = "ROUTER"
  type Tunnel (line 11) | type Tunnel struct
    method Name (line 13) | func (t *Tunnel) Name() string {
    method NewClient (line 17) | func (t *Tunnel) NewClient(ctx context.Context, client tunnel.Client) ...
    method NewServer (line 21) | func (t *Tunnel) NewServer(ctx context.Context, server tunnel.Server) ...
  function init (line 25) | func init() {

FILE: tunnel/shadowsocks/client.go
  type Client (line 14) | type Client struct
    method DialConn (line 19) | func (c *Client) DialConn(address *tunnel.Address, tunnel tunnel.Tunne...
    method DialPacket (line 30) | func (c *Client) DialPacket(tunnel tunnel.Tunnel) (tunnel.PacketConn, ...
    method Close (line 34) | func (c *Client) Close() error {
  function NewClient (line 38) | func NewClient(ctx context.Context, underlay tunnel.Client) (*Client, er...

FILE: tunnel/shadowsocks/config.go
  type ShadowsocksConfig (line 5) | type ShadowsocksConfig struct
  type Config (line 11) | type Config struct
  function init (line 17) | func init() {

FILE: tunnel/shadowsocks/conn.go
  type Conn (line 9) | type Conn struct
    method Read (line 14) | func (c *Conn) Read(p []byte) (n int, err error) {
    method Write (line 18) | func (c *Conn) Write(p []byte) (n int, err error) {
    method Close (line 22) | func (c *Conn) Close() error {
    method Metadata (line 27) | func (c *Conn) Metadata() *tunnel.Metadata {

FILE: tunnel/shadowsocks/server.go
  type Server (line 16) | type Server struct
    method AcceptConn (line 23) | func (s *Server) AcceptConn(overlay tunnel.Tunnel) (tunnel.Conn, error) {
    method AcceptPacket (line 55) | func (s *Server) AcceptPacket(t tunnel.Tunnel) (tunnel.PacketConn, err...
    method Close (line 59) | func (s *Server) Close() error {
  function NewServer (line 63) | func NewServer(ctx context.Context, underlay tunnel.Server) (*Server, er...

FILE: tunnel/shadowsocks/shadowsocks_test.go
  function TestShadowsocks (line 19) | func TestShadowsocks(t *testing.T) {

FILE: tunnel/shadowsocks/tunnel.go
  constant Name (line 9) | Name = "SHADOWSOCKS"
  type Tunnel (line 11) | type Tunnel struct
    method Name (line 13) | func (t *Tunnel) Name() string {
    method NewClient (line 17) | func (t *Tunnel) NewClient(ctx context.Context, client tunnel.Client) ...
    method NewServer (line 21) | func (t *Tunnel) NewServer(ctx context.Context, server tunnel.Server) ...
  function init (line 25) | func init() {

FILE: tunnel/simplesocks/client.go
  constant Connect (line 13) | Connect   tunnel.Command = 1
  constant Associate (line 14) | Associate tunnel.Command = 3
  type Client (line 17) | type Client struct
    method DialConn (line 21) | func (c *Client) DialConn(addr *tunnel.Address, t tunnel.Tunnel) (tunn...
    method DialPacket (line 36) | func (c *Client) DialPacket(t tunnel.Tunnel) (tunnel.PacketConn, error) {
    method Close (line 58) | func (c *Client) Close() error {
  function NewClient (line 62) | func NewClient(ctx context.Context, underlay tunnel.Client) (*Client, er...

FILE: tunnel/simplesocks/conn.go
  type Conn (line 12) | type Conn struct
    method Metadata (line 19) | func (c *Conn) Metadata() *tunnel.Metadata {
    method Write (line 23) | func (c *Conn) Write(payload []byte) (int, error) {
  type PacketConn (line 40) | type PacketConn struct

FILE: tunnel/simplesocks/server.go
  type Server (line 14) | type Server struct
    method Close (line 22) | func (s *Server) Close() error {
    method acceptLoop (line 27) | func (s *Server) acceptLoop() {
    method AcceptConn (line 64) | func (s *Server) AcceptConn(tunnel.Tunnel) (tunnel.Conn, error) {
    method AcceptPacket (line 73) | func (s *Server) AcceptPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
  function NewServer (line 82) | func NewServer(ctx context.Context, underlay tunnel.Server) (*Server, er...

FILE: tunnel/simplesocks/simplesocks_test.go
  function TestSimpleSocks (line 16) | func TestSimpleSocks(t *testing.T) {

FILE: tunnel/simplesocks/tunnel.go
  constant Name (line 9) | Name = "SIMPLESOCKS"
  type Tunnel (line 11) | type Tunnel struct
    method Name (line 13) | func (*Tunnel) Name() string {
    method NewServer (line 17) | func (*Tunnel) NewServer(ctx context.Context, underlay tunnel.Server) ...
    method NewClient (line 21) | func (*Tunnel) NewClient(ctx context.Context, underlay tunnel.Client) ...
  function init (line 25) | func init() {

FILE: tunnel/socks/config.go
  type Config (line 5) | type Config struct
  function init (line 11) | func init() {

FILE: tunnel/socks/conn.go
  type Conn (line 11) | type Conn struct
    method Metadata (line 16) | func (c *Conn) Metadata() *tunnel.Metadata {
  type packetInfo (line 20) | type packetInfo struct
  type PacketConn (line 25) | type PacketConn struct
    method ReadFrom (line 34) | func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err err...
    method WriteTo (line 38) | func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro...
    method Close (line 42) | func (c *PacketConn) Close() error {
    method WriteWithMetadata (line 47) | func (c *PacketConn) WriteWithMetadata(p []byte, m *tunnel.Metadata) (...
    method ReadWithMetadata (line 59) | func (c *PacketConn) ReadWithMetadata(p []byte) (int, *tunnel.Metadata...

FILE: tunnel/socks/server.go
  constant Connect (line 20) | Connect   tunnel.Command = 1
  constant Associate (line 21) | Associate tunnel.Command = 3
  constant MaxPacketSize (line 25) | MaxPacketSize = 1024 * 8
  type Server (line 28) | type Server struct
    method AcceptConn (line 42) | func (s *Server) AcceptConn(tunnel.Tunnel) (tunnel.Conn, error) {
    method AcceptPacket (line 51) | func (s *Server) AcceptPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
    method Close (line 60) | func (s *Server) Close() error {
    method handshake (line 65) | func (s *Server) handshake(conn net.Conn) (*Conn, error) {
    method connect (line 103) | func (s *Server) connect(conn net.Conn) error {
    method associate (line 108) | func (s *Server) associate(conn net.Conn, addr *tunnel.Address) error {
    method packetDispatchLoop (line 115) | func (s *Server) packetDispatchLoop() {
    method acceptLoop (line 198) | func (s *Server) acceptLoop() {
  function NewServer (line 240) | func NewServer(ctx context.Context, underlay tunnel.Server) (tunnel.Serv...

FILE: tunnel/socks/socks_test.go
  function TestSocks (line 24) | func TestSocks(t *testing.T) {

FILE: tunnel/socks/tunnel.go
  constant Name (line 9) | Name = "SOCKS"
  type Tunnel (line 11) | type Tunnel struct
    method Name (line 13) | func (*Tunnel) Name() string {
    method NewClient (line 17) | func (*Tunnel) NewClient(context.Context, tunnel.Client) (tunnel.Clien...
    method NewServer (line 21) | func (*Tunnel) NewServer(ctx context.Context, server tunnel.Server) (t...
  function init (line 25) | func init() {

FILE: tunnel/tls/client.go
  type Client (line 23) | type Client struct
    method Close (line 36) | func (c *Client) Close() error {
    method DialPacket (line 43) | func (c *Client) DialPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
    method DialConn (line 47) | func (c *Client) DialConn(_ *tunnel.Address, overlay tunnel.Tunnel) (t...
  function NewClient (line 87) | func NewClient(ctx context.Context, underlay tunnel.Client) (*Client, er...

FILE: tunnel/tls/config.go
  type Config (line 7) | type Config struct
  type WebsocketConfig (line 14) | type WebsocketConfig struct
  type TLSConfig (line 18) | type TLSConfig struct
  function init (line 38) | func init() {

FILE: tunnel/tls/fingerprint/tls.go
  function ParseCipher (line 9) | func ParseCipher(s []string) []uint16 {

FILE: tunnel/tls/server.go
  type Server (line 31) | type Server struct
    method Close (line 54) | func (s *Server) Close() error {
    method acceptLoop (line 71) | func (s *Server) acceptLoop() {
    method AcceptConn (line 181) | func (s *Server) AcceptConn(overlay tunnel.Tunnel) (tunnel.Conn, error) {
    method AcceptPacket (line 202) | func (s *Server) AcceptPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
    method checkKeyPairLoop (line 206) | func (s *Server) checkKeyPairLoop(checkRate time.Duration, keyPath str...
  function isDomainNameMatched (line 62) | func isDomainNameMatched(pattern string, domainName string) bool {
  function loadKeyPair (line 247) | func loadKeyPair(keyPath string, certPath string, password string) (*tls...
  function NewServer (line 291) | func NewServer(ctx context.Context, underlay tunnel.Server) (*Server, er...

FILE: tunnel/tls/tls_test.go
  function TestDefaultTLSRSA2048 (line 95) | func TestDefaultTLSRSA2048(t *testing.T) {
  function TestDefaultTLSECC (line 157) | func TestDefaultTLSECC(t *testing.T) {
  function TestUTLSRSA2048 (line 219) | func TestUTLSRSA2048(t *testing.T) {
  function TestUTLSECC (line 289) | func TestUTLSECC(t *testing.T) {
  function TestMatch (line 359) | func TestMatch(t *testing.T) {

FILE: tunnel/tls/tunnel.go
  constant Name (line 9) | Name = "TLS"
  type Tunnel (line 11) | type Tunnel struct
    method Name (line 13) | func (t *Tunnel) Name() string {
    method NewClient (line 17) | func (t *Tunnel) NewClient(ctx context.Context, client tunnel.Client) ...
    method NewServer (line 21) | func (t *Tunnel) NewServer(ctx context.Context, server tunnel.Server) ...
  function init (line 25) | func init() {

FILE: tunnel/tproxy/config.go
  type Config (line 8) | type Config struct
  function init (line 14) | func init() {

FILE: tunnel/tproxy/conn.go
  type Conn (line 14) | type Conn struct
    method Metadata (line 19) | func (c *Conn) Metadata() *tunnel.Metadata {
  type packetInfo (line 23) | type packetInfo struct
  type PacketConn (line 28) | type PacketConn struct
    method ReadFrom (line 37) | func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err err...
    method WriteTo (line 41) | func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro...
    method Close (line 45) | func (c *PacketConn) Close() error {
    method WriteWithMetadata (line 50) | func (c *PacketConn) WriteWithMetadata(p []byte, m *tunnel.Metadata) (...
    method ReadWithMetadata (line 62) | func (c *PacketConn) ReadWithMetadata(p []byte) (int, *tunnel.Metadata...

FILE: tunnel/tproxy/getsockopt.go
  function getsockopt (line 11) | func getsockopt(fd int, level int, optname int, optval unsafe.Pointer, o...

FILE: tunnel/tproxy/getsockopt_i386.go
  constant GETSOCKOPT (line 11) | GETSOCKOPT = 15
  function getsockopt (line 13) | func getsockopt(fd int, level int, optname int, optval unsafe.Pointer, o...

FILE: tunnel/tproxy/server.go
  constant MaxPacketSize (line 19) | MaxPacketSize = 1024 * 8
  type Server (line 21) | type Server struct
    method Close (line 32) | func (s *Server) Close() error {
    method AcceptConn (line 38) | func (s *Server) AcceptConn(tunnel.Tunnel) (tunnel.Conn, error) {
    method packetDispatchLoop (line 63) | func (s *Server) packetDispatchLoop() {
    method AcceptPacket (line 185) | func (s *Server) AcceptPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
  function NewServer (line 195) | func NewServer(ctx context.Context, _ tunnel.Server) (*Server, error) {

FILE: tunnel/tproxy/tcp.go
  type Listener (line 17) | type Listener struct
    method Accept (line 26) | func (listener *Listener) Accept() (net.Conn, error) {
    method Addr (line 38) | func (listener *Listener) Addr() net.Addr {
    method Close (line 45) | func (listener *Listener) Close() error {
  function ListenTCP (line 52) | func ListenTCP(network string, laddr *net.TCPAddr) (net.Listener, error) {
  constant IP6T_SO_ORIGINAL_DST (line 72) | IP6T_SO_ORIGINAL_DST = 80
  constant SO_ORIGINAL_DST (line 73) | SO_ORIGINAL_DST      = 80
  function getOriginalTCPDest (line 83) | func getOriginalTCPDest(conn *net.TCPConn) (*net.TCPAddr, error) {

FILE: tunnel/tproxy/tunnel.go
  constant Name (line 12) | Name = "TPROXY"
  type Tunnel (line 14) | type Tunnel struct
    method Name (line 16) | func (t *Tunnel) Name() string {
    method NewClient (line 20) | func (t *Tunnel) NewClient(ctx context.Context, client tunnel.Client) ...
    method NewServer (line 24) | func (t *Tunnel) NewServer(ctx context.Context, server tunnel.Server) ...
  function init (line 28) | func init() {

FILE: tunnel/tproxy/udp.go
  function ListenUDP (line 20) | func ListenUDP(network string, laddr *net.UDPAddr) (*net.UDPConn, error) {
  function ReadFromUDP (line 50) | func ReadFromUDP(conn *net.UDPConn, b []byte) (int, *net.UDPAddr, *net.U...
  function DialUDP (line 104) | func DialUDP(network string, laddr *net.UDPAddr, raddr *net.UDPAddr) (*n...
  function udpAddrToSocketAddr (line 155) | func udpAddrToSocketAddr(addr *net.UDPAddr) (syscall.Sockaddr, error) {
  function udpAddrFamily (line 179) | func udpAddrFamily(net string, laddr, raddr *net.UDPAddr) int {

FILE: tunnel/transport/client.go
  type Client (line 17) | type Client struct
    method Close (line 25) | func (c *Client) Close() error {
    method DialPacket (line 33) | func (c *Client) DialPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
    method DialConn (line 38) | func (c *Client) DialConn(*tunnel.Address, tunnel.Tunnel) (tunnel.Conn...
  function NewClient (line 49) | func NewClient(ctx context.Context, _ tunnel.Client) (*Client, error) {

FILE: tunnel/transport/config.go
  type Config (line 7) | type Config struct
  type TransportPluginConfig (line 15) | type TransportPluginConfig struct
  function init (line 24) | func init() {

FILE: tunnel/transport/conn.go
  type Conn (line 9) | type Conn struct
    method Metadata (line 13) | func (c *Conn) Metadata() *tunnel.Metadata {

FILE: tunnel/transport/server.go
  type Server (line 21) | type Server struct
    method Close (line 32) | func (s *Server) Close() error {
    method acceptLoop (line 40) | func (s *Server) acceptLoop() {
    method AcceptConn (line 89) | func (s *Server) AcceptConn(overlay tunnel.Tunnel) (tunnel.Conn, error) {
    method AcceptPacket (line 110) | func (s *Server) AcceptPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
  function NewServer (line 115) | func NewServer(ctx context.Context, _ tunnel.Server) (*Server, error) {

FILE: tunnel/transport/transport_test.go
  function TestTransport (line 15) | func TestTransport(t *testing.T) {
  function TestClientPlugin (line 60) | func TestClientPlugin(t *testing.T) {
  function TestServerPlugin (line 83) | func TestServerPlugin(t *testing.T) {

FILE: tunnel/transport/tunnel.go
  constant Name (line 9) | Name = "TRANSPORT"
  type Tunnel (line 11) | type Tunnel struct
    method Name (line 13) | func (*Tunnel) Name() string {
    method NewClient (line 17) | func (*Tunnel) NewClient(ctx context.Context, client tunnel.Client) (t...
    method NewServer (line 21) | func (*Tunnel) NewServer(ctx context.Context, server tunnel.Server) (t...
  function init (line 25) | func init() {

FILE: tunnel/trojan/client.go
  constant MaxPacketSize (line 22) | MaxPacketSize = 1024 * 8
  constant Connect (line 26) | Connect   tunnel.Command = 1
  constant Associate (line 27) | Associate tunnel.Command = 3
  constant Mux (line 28) | Mux       tunnel.Command = 0x7f
  type OutboundConn (line 31) | type OutboundConn struct
    method Metadata (line 46) | func (c *OutboundConn) Metadata() *tunnel.Metadata {
    method WriteHeader (line 50) | func (c *OutboundConn) WriteHeader(payload []byte) (bool, error) {
    method Write (line 72) | func (c *OutboundConn) Write(p []byte) (int, error) {
    method Read (line 86) | func (c *OutboundConn) Read(p []byte) (int, error) {
    method Close (line 93) | func (c *OutboundConn) Close() error {
  type Client (line 98) | type Client struct
    method Close (line 105) | func (c *Client) Close() error {
    method DialConn (line 110) | func (c *Client) DialConn(addr *tunnel.Address, overlay tunnel.Tunnel)...
    method DialPacket (line 136) | func (c *Client) DialPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
  function NewClient (line 157) | func NewClient(ctx context.Context, client tunnel.Client) (*Client, erro...

FILE: tunnel/trojan/config.go
  type Config (line 5) | type Config struct
  type MySQLConfig (line 15) | type MySQLConfig struct
  type APIConfig (line 19) | type APIConfig struct
  function init (line 23) | func init() {

FILE: tunnel/trojan/packet.go
  type PacketConn (line 15) | type PacketConn struct
    method ReadFrom (line 19) | func (c *PacketConn) ReadFrom(payload []byte) (int, net.Addr, error) {
    method WriteTo (line 23) | func (c *PacketConn) WriteTo(payload []byte, addr net.Addr) (int, erro...
    method WriteWithMetadata (line 34) | func (c *PacketConn) WriteWithMetadata(payload []byte, metadata *tunne...
    method ReadWithMetadata (line 54) | func (c *PacketConn) ReadWithMetadata(payload []byte) (int, *tunnel.Me...

FILE: tunnel/trojan/server.go
  type InboundConn (line 23) | type InboundConn struct
    method Metadata (line 40) | func (c *InboundConn) Metadata() *tunnel.Metadata {
    method Write (line 44) | func (c *InboundConn) Write(p []byte) (int, error) {
    method Read (line 51) | func (c *InboundConn) Read(p []byte) (int, error) {
    method Close (line 58) | func (c *InboundConn) Close() error {
    method Auth (line 65) | func (c *InboundConn) Auth() error {
  type Server (line 109) | type Server struct
    method Close (line 121) | func (s *Server) Close() error {
    method acceptLoop (line 126) | func (s *Server) acceptLoop() {
    method AcceptConn (line 185) | func (s *Server) AcceptConn(nextTunnel tunnel.Tunnel) (tunnel.Conn, er...
    method AcceptPacket (line 204) | func (s *Server) AcceptPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
  function NewServer (line 213) | func NewServer(ctx context.Context, underlay tunnel.Server) (*Server, er...

FILE: tunnel/trojan/trojan_test.go
  function TestTrojan (line 20) | func TestTrojan(t *testing.T) {

FILE: tunnel/trojan/tunnel.go
  constant Name (line 9) | Name = "TROJAN"
  type Tunnel (line 11) | type Tunnel struct
    method Name (line 13) | func (c *Tunnel) Name() string {
    method NewClient (line 17) | func (c *Tunnel) NewClient(ctx context.Context, client tunnel.Client) ...
    method NewServer (line 21) | func (c *Tunnel) NewServer(ctx context.Context, server tunnel.Server) ...
  function init (line 25) | func init() {

FILE: tunnel/tunnel.go
  type Conn (line 12) | type Conn interface
  type PacketConn (line 18) | type PacketConn interface
  type ConnDialer (line 25) | type ConnDialer interface
  type PacketDialer (line 30) | type PacketDialer interface
  type ConnListener (line 35) | type ConnListener interface
  type PacketListener (line 41) | type PacketListener interface
  type Dialer (line 46) | type Dialer interface
  type Listener (line 52) | type Listener interface
  type Client (line 58) | type Client interface
  type Server (line 64) | type Server interface
  type Tunnel (line 71) | type Tunnel interface
  function RegisterTunnel (line 80) | func RegisterTunnel(name string, tunnel Tunnel) {
  function GetTunnel (line 84) | func GetTunnel(name string) (Tunnel, error) {

FILE: tunnel/websocket/client.go
  type Client (line 15) | type Client struct
    method DialConn (line 21) | func (c *Client) DialConn(*tunnel.Address, tunnel.Tunnel) (tunnel.Conn...
    method DialPacket (line 42) | func (c *Client) DialPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
    method Close (line 46) | func (c *Client) Close() error {
  function NewClient (line 50) | func NewClient(ctx context.Context, underlay tunnel.Client) (*Client, er...

FILE: tunnel/websocket/config.go
  type WebsocketConfig (line 5) | type WebsocketConfig struct
  type Config (line 11) | type Config struct
  function init (line 17) | func init() {

FILE: tunnel/websocket/conn.go
  type OutboundConn (line 12) | type OutboundConn struct
    method Metadata (line 17) | func (c *OutboundConn) Metadata() *tunnel.Metadata {
    method RemoteAddr (line 21) | func (c *OutboundConn) RemoteAddr() net.Addr {
  type InboundConn (line 26) | type InboundConn struct
    method Close (line 32) | func (c *InboundConn) Close() error {

FILE: tunnel/websocket/server.go
  type fakeHTTPResponseWriter (line 23) | type fakeHTTPResponseWriter struct
    method Hijack (line 31) | func (w *fakeHTTPResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter...
  type Server (line 35) | type Server struct
    method Close (line 47) | func (s *Server) Close() error {
    method AcceptConn (line 52) | func (s *Server) AcceptConn(tunnel.Tunnel) (tunnel.Conn, error) {
    method AcceptPacket (line 146) | func (s *Server) AcceptPacket(tunnel.Tunnel) (tunnel.PacketConn, error) {
  function NewServer (line 150) | func NewServer(ctx context.Context, underlay tunnel.Server) (*Server, er...

FILE: tunnel/websocket/tunnel.go
  constant Name (line 9) | Name = "WEBSOCKET"
  type Tunnel (line 11) | type Tunnel struct
    method Name (line 13) | func (*Tunnel) Name() string {
    method NewServer (line 17) | func (*Tunnel) NewServer(ctx context.Context, underlay tunnel.Server) ...
    method NewClient (line 21) | func (*Tunnel) NewClient(ctx context.Context, underlay tunnel.Client) ...
  function init (line 25) | func init() {

FILE: tunnel/websocket/websocket_test.go
  function TestWebsocket (line 22) | func TestWebsocket(t *testing.T) {
  function TestRedirect (line 80) | func TestRedirect(t *testing.T) {

FILE: url/option.go
  constant Name (line 16) | Name = "URL"
  type Websocket (line 18) | type Websocket struct
  type TLS (line 24) | type TLS struct
  type Shadowsocks (line 28) | type Shadowsocks struct
  type Mux (line 34) | type Mux struct
  type API (line 38) | type API struct
  type UrlConfig (line 44) | type UrlConfig struct
  type url (line 58) | type url struct
    method Name (line 63) | func (u *url) Name() string {
    method Handle (line 67) | func (u *url) Handle() error {
    method Priority (line 183) | func (u *url) Priority() int {
  function init (line 187) | func init() {

FILE: url/option_test.go
  function TestUrl_Handle (line 10) | func TestUrl_Handle(t *testing.T) {

FILE: url/share_link.go
  constant ShareInfoTypeOriginal (line 12) | ShareInfoTypeOriginal  = "original"
  constant ShareInfoTypeWebSocket (line 13) | ShareInfoTypeWebSocket = "ws"
  type ShareInfo (line 32) | type ShareInfo struct
  function NewShareInfoFromURL (line 48) | func NewShareInfoFromURL(shareLink string) (info ShareInfo, e error) {
  function handleTrojanPort (line 203) | func handleTrojanPort(p string) (port uint16, e error) {

FILE: url/share_link_test.go
  function TestHandleTrojanPort_Default (line 13) | func TestHandleTrojanPort_Default(t *testing.T) {
  function TestHandleTrojanPort_NotNumber (line 19) | func TestHandleTrojanPort_NotNumber(t *testing.T) {
  function TestHandleTrojanPort_GoodNumber (line 24) | func TestHandleTrojanPort_GoodNumber(t *testing.T) {
  function TestHandleTrojanPort_InvalidNumber (line 32) | func TestHandleTrojanPort_InvalidNumber(t *testing.T) {
  function TestNewShareInfoFromURL_Empty (line 41) | func TestNewShareInfoFromURL_Empty(t *testing.T) {
  function TestNewShareInfoFromURL_RandomCrap (line 46) | func TestNewShareInfoFromURL_RandomCrap(t *testing.T) {
  function TestNewShareInfoFromURL_NotTrojanGo (line 54) | func TestNewShareInfoFromURL_NotTrojanGo(t *testing.T) {
  function TestNewShareInfoFromURL_EmptyTrojanHost (line 67) | func TestNewShareInfoFromURL_EmptyTrojanHost(t *testing.T) {
  function TestNewShareInfoFromURL_BadPassword (line 72) | func TestNewShareInfoFromURL_BadPassword(t *testing.T) {
  function TestNewShareInfoFromURL_GoodPassword (line 87) | func TestNewShareInfoFromURL_GoodPassword(t *testing.T) {
  function TestNewShareInfoFromURL_BadPort (line 100) | func TestNewShareInfoFromURL_BadPort(t *testing.T) {
  function TestNewShareInfoFromURL_BadQuery (line 115) | func TestNewShareInfoFromURL_BadQuery(t *testing.T) {
  function TestNewShareInfoFromURL_SNI_Empty (line 127) | func TestNewShareInfoFromURL_SNI_Empty(t *testing.T) {
  function TestNewShareInfoFromURL_SNI_Default (line 132) | func TestNewShareInfoFromURL_SNI_Default(t *testing.T) {
  function TestNewShareInfoFromURL_SNI_Multiple (line 138) | func TestNewShareInfoFromURL_SNI_Multiple(t *testing.T) {
  function TestNewShareInfoFromURL_Type_Empty (line 143) | func TestNewShareInfoFromURL_Type_Empty(t *testing.T) {
  function TestNewShareInfoFromURL_Type_Default (line 148) | func TestNewShareInfoFromURL_Type_Default(t *testing.T) {
  function TestNewShareInfoFromURL_Type_Invalid (line 154) | func TestNewShareInfoFromURL_Type_Invalid(t *testing.T) {
  function TestNewShareInfoFromURL_Type_Multiple (line 162) | func TestNewShareInfoFromURL_Type_Multiple(t *testing.T) {
  function TestNewShareInfoFromURL_Host_Empty (line 167) | func TestNewShareInfoFromURL_Host_Empty(t *testing.T) {
  function TestNewShareInfoFromURL_Host_Default (line 172) | func TestNewShareInfoFromURL_Host_Default(t *testing.T) {
  function TestNewShareInfoFromURL_Host_Multiple (line 178) | func TestNewShareInfoFromURL_Host_Multiple(t *testing.T) {
  function TestNewShareInfoFromURL_Type_WS_Multiple (line 183) | func TestNewShareInfoFromURL_Type_WS_Multiple(t *testing.T) {
  function TestNewShareInfoFromURL_Path_WS_None (line 188) | func TestNewShareInfoFromURL_Path_WS_None(t *testing.T) {
  function TestNewShareInfoFromURL_Path_WS_Empty (line 193) | func TestNewShareInfoFromURL_Path_WS_Empty(t *testing.T) {
  function TestNewShareInfoFromURL_Path_WS_Invalid (line 198) | func TestNewShareInfoFromURL_Path_WS_Invalid(t *testing.T) {
  function TestNewShareInfoFromURL_Path_Plain_Empty (line 206) | func TestNewShareInfoFromURL_Path_Plain_Empty(t *testing.T) {
  function TestNewShareInfoFromURL_Encryption_Empty (line 211) | func TestNewShareInfoFromURL_Encryption_Empty(t *testing.T) {
  function TestNewShareInfoFromURL_Encryption_Unknown (line 216) | func TestNewShareInfoFromURL_Encryption_Unknown(t *testing.T) {
  function TestNewShareInfoFromURL_Encryption_None (line 221) | func TestNewShareInfoFromURL_Encryption_None(t *testing.T) {
  function TestNewShareInfoFromURL_Encryption_SS_NotSupportedMethods (line 226) | func TestNewShareInfoFromURL_Encryption_SS_NotSupportedMethods(t *testin...
  function TestNewShareInfoFromURL_Encryption_SS_NoPassword (line 234) | func TestNewShareInfoFromURL_Encryption_SS_NoPassword(t *testing.T) {
  function TestNewShareInfoFromURL_Encryption_SS_BadParams (line 239) | func TestNewShareInfoFromURL_Encryption_SS_BadParams(t *testing.T) {
  function TestNewShareInfoFromURL_Encryption_Multiple (line 244) | func TestNewShareInfoFromURL_Encryption_Multiple(t *testing.T) {
  function TestNewShareInfoFromURL_Plugin_Empty (line 249) | func TestNewShareInfoFromURL_Plugin_Empty(t *testing.T) {
  function TestNewShareInfoFromURL_Plugin_Multiple (line 254) | func TestNewShareInfoFromURL_Plugin_Multiple(t *testing.T) {

FILE: version/version.go
  type versionOption (line 13) | type versionOption struct
    method Name (line 17) | func (*versionOption) Name() string {
    method Priority (line 21) | func (*versionOption) Priority() int {
    method Handle (line 25) | func (c *versionOption) Handle() error {
  function init (line 41) | func init() {
Condensed preview — 205 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (582K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.md",
    "chars": 461,
    "preview": "---\nname: Bug 报告\nabout: 提交项目中存在的漏洞和问题\ntitle: \"[BUG]\"\nlabels: ''\nassignees: ''\n\n---\n\n- [ ] **我确定我已经尝试多次复现此次问题,并且将会提供涉及此问题"
  },
  {
    "path": ".github/linters/.golangci.yml",
    "chars": 646,
    "preview": "run:\n  timeout: 5m\n  skip-files:\n    - \\.pb\\.go$\n\nissues:\n  new: true\n\nlinters:\n  enable:\n    - asciicheck\n    - bodyclo"
  },
  {
    "path": ".github/workflows/docker-build.yml",
    "chars": 1901,
    "preview": "on:\n  push:\n    branches:\n      - master\n    paths-ignore:\n      - \"**.md\"\n      - \"docs/**\"\n    tags:\n      - \"v*.*.*\"\n"
  },
  {
    "path": ".github/workflows/docker-nightly-build.yml",
    "chars": 1515,
    "preview": "on:\n  push:\n    branches:\n      - master\n    paths-ignore:\n      - '**.md'\n      - 'docs/**'\nname: docker-nightly-build\n"
  },
  {
    "path": ".github/workflows/gh-pages.yml",
    "chars": 964,
    "preview": "on:\n  push:\n    branches:\n      - master\n    paths:\n      - \"docs/**\"\n      - \".github/workflows/gh-pages.yml\"\n  pull_re"
  },
  {
    "path": ".github/workflows/linter.yml",
    "chars": 681,
    "preview": "name: Linter\n\non:\n  push:\n    branches:\n      - master\n    paths:\n      - \"**/*.go\"\n      - \".github/workflows/linter.ym"
  },
  {
    "path": ".github/workflows/nightly-build.yml",
    "chars": 731,
    "preview": "on:\n  push:\n    branches:\n      - master\n    paths:\n      - \"**/*.go\"\n      - \"go.mod\"\n      - \"go.sum\"\n      - \"Makefil"
  },
  {
    "path": ".github/workflows/release-build.yml",
    "chars": 865,
    "preview": "on:\n  push:\n    tags:\n      - \"v*.*.*\"\nname: release-build\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - "
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 876,
    "preview": "name: Test\non:\n  push:\n    branches:\n      - master\n    paths:\n      - \"**/*.go\"\n      - \"go.mod\"\n      - \"go.sum\"\n     "
  },
  {
    "path": ".gitignore",
    "chars": 331,
    "preview": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, built with `go test -c`\n*.test\n\n# Ou"
  },
  {
    "path": "Dockerfile",
    "chars": 961,
    "preview": "FROM golang:alpine AS builder\nWORKDIR /\nARG REF\nRUN apk add git make &&\\\n    git clone https://github.com/p4gefau1t/troj"
  },
  {
    "path": "LICENSE",
    "chars": 35149,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
  },
  {
    "path": "Makefile",
    "chars": 4535,
    "preview": "NAME := trojan-go\nPACKAGE_NAME := github.com/p4gefau1t/trojan-go\nVERSION := `git describe --dirty`\nCOMMIT := `git rev-pa"
  },
  {
    "path": "README.md",
    "chars": 7675,
    "preview": "# Trojan-Go [![Go Report Card](https://goreportcard.com/badge/github.com/p4gefau1t/trojan-go)](https://goreportcard.com/"
  },
  {
    "path": "api/api.go",
    "chars": 560,
    "preview": "package api\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/log\"\n\t\"github.com/p4gefau1t/trojan-go/statistic\"\n)\n\nt"
  },
  {
    "path": "api/control/control.go",
    "chars": 4391,
    "preview": "package control\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"google.golang.org/grpc\"\n\n\t\"github.com/p4ge"
  },
  {
    "path": "api/service/api.pb.go",
    "chars": 36174,
    "preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.27.1\n// \tprotoc        v3.17.3\n// sou"
  },
  {
    "path": "api/service/api.proto",
    "chars": 1630,
    "preview": "syntax = \"proto3\";\n\npackage trojan.api;\noption go_package = \"github.com/p4gefau1t/trojan-go/api/service\";\n\nmessage Traff"
  },
  {
    "path": "api/service/api_grpc.pb.go",
    "chars": 12256,
    "preview": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n\npackage service\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang"
  },
  {
    "path": "api/service/client.go",
    "chars": 2337,
    "preview": "package service\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"github.com/p4gefau1t/trojan-go/api\"\n\t\"github.com/p4gefau1t/trojan-go/comm"
  },
  {
    "path": "api/service/client_test.go",
    "chars": 1333,
    "preview": "package service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"google.golang.org/grpc\"\n\n\t\"github.com/p4gefau1t/trojan"
  },
  {
    "path": "api/service/config.go",
    "chars": 799,
    "preview": "package service\n\nimport \"github.com/p4gefau1t/trojan-go/config\"\n\nconst Name = \"API_SERVICE\"\n\ntype SSLConfig struct {\n\tEn"
  },
  {
    "path": "api/service/gen.sh",
    "chars": 643,
    "preview": "#!/usr/bin/env bash\n\necho \"Processing...\"\n\nGOPATH=${GOPATH:-$(go env GOPATH)}\nGOBIN=${GOBIN:-$(go env GOBIN)}\n\nif [[ $GO"
  },
  {
    "path": "api/service/server.go",
    "chars": 6885,
    "preview": "package service\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net\"\n\n\t\"google.golang.org/grpc\"\n\t"
  },
  {
    "path": "api/service/server_test.go",
    "chars": 14430,
    "preview": "package service\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"google.golang.org/g"
  },
  {
    "path": "common/common.go",
    "chars": 888,
    "preview": "package common\n\nimport (\n\t\"crypto/sha256\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/p4gefau1t/trojan-go/log\"\n)\n\ntype R"
  },
  {
    "path": "common/error.go",
    "chars": 471,
    "preview": "package common\n\nimport (\n\t\"fmt\"\n)\n\ntype Error struct {\n\tinfo string\n}\n\nfunc (e *Error) Error() string {\n\treturn e.info\n}"
  },
  {
    "path": "common/geodata/cache.go",
    "chars": 3569,
    "preview": "package geodata\n\nimport (\n\t\"io/ioutil\"\n\t\"strings\"\n\n\tv2router \"github.com/v2fly/v2ray-core/v4/app/router\"\n\t\"google.golang"
  },
  {
    "path": "common/geodata/decode.go",
    "chars": 3269,
    "preview": "// Package geodata includes utilities to decode and parse the geoip & geosite dat files.\n//\n// It relies on the proto st"
  },
  {
    "path": "common/geodata/decode_test.go",
    "chars": 3044,
    "preview": "package geodata_test\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"testing\"\n\n\t\"github.com/p4"
  },
  {
    "path": "common/geodata/interface.go",
    "chars": 339,
    "preview": "package geodata\n\nimport v2router \"github.com/v2fly/v2ray-core/v4/app/router\"\n\ntype GeodataLoader interface {\n\tLoadIP(fil"
  },
  {
    "path": "common/geodata/loader.go",
    "chars": 979,
    "preview": "package geodata\n\nimport (\n\t\"runtime\"\n\n\tv2router \"github.com/v2fly/v2ray-core/v4/app/router\"\n)\n\ntype geodataCache struct "
  },
  {
    "path": "common/io.go",
    "chars": 2516,
    "preview": "package common\n\nimport (\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\n\t\"github.com/p4gefau1t/trojan-go/log\"\n)\n\ntype RewindReader struct {\n\tmu  "
  },
  {
    "path": "common/io_test.go",
    "chars": 709,
    "preview": "package common\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"testing\"\n\n\t\"github.com/v2fly/v2ray-core/v4/common\"\n)\n\nfunc TestBuffer"
  },
  {
    "path": "common/net.go",
    "chars": 2431,
    "preview": "package common\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\n"
  },
  {
    "path": "common/sync.go",
    "chars": 640,
    "preview": "package common\n\n// Notifier is a utility for notifying changes. The change producer may notify changes multiple time, an"
  },
  {
    "path": "component/api.go",
    "chars": 165,
    "preview": "//go:build api || full\n// +build api full\n\npackage build\n\nimport (\n\t_ \"github.com/p4gefau1t/trojan-go/api/control\"\n\t_ \"g"
  },
  {
    "path": "component/base.go",
    "chars": 169,
    "preview": "package build\n\nimport (\n\t_ \"github.com/p4gefau1t/trojan-go/log/golog\"\n\t_ \"github.com/p4gefau1t/trojan-go/statistic/memor"
  },
  {
    "path": "component/client.go",
    "chars": 137,
    "preview": "//go:build client || full || mini\n// +build client full mini\n\npackage build\n\nimport (\n\t_ \"github.com/p4gefau1t/trojan-go"
  },
  {
    "path": "component/custom.go",
    "chars": 835,
    "preview": "//go:build custom || full\n// +build custom full\n\npackage build\n\nimport (\n\t_ \"github.com/p4gefau1t/trojan-go/proxy/custom"
  },
  {
    "path": "component/forward.go",
    "chars": 140,
    "preview": "//go:build forward || full || mini\n// +build forward full mini\n\npackage build\n\nimport (\n\t_ \"github.com/p4gefau1t/trojan-"
  },
  {
    "path": "component/mysql.go",
    "chars": 138,
    "preview": "//go:build mysql || full || mini\n// +build mysql full mini\n\npackage build\n\nimport (\n\t_ \"github.com/p4gefau1t/trojan-go/s"
  },
  {
    "path": "component/nat.go",
    "chars": 128,
    "preview": "//go:build nat || full || mini\n// +build nat full mini\n\npackage build\n\nimport (\n\t_ \"github.com/p4gefau1t/trojan-go/proxy"
  },
  {
    "path": "component/other.go",
    "chars": 154,
    "preview": "//go:build other || full\n// +build other full\n\npackage build\n\nimport (\n\t_ \"github.com/p4gefau1t/trojan-go/easy\"\n\t_ \"gith"
  },
  {
    "path": "component/server.go",
    "chars": 137,
    "preview": "//go:build server || full || mini\n// +build server full mini\n\npackage build\n\nimport (\n\t_ \"github.com/p4gefau1t/trojan-go"
  },
  {
    "path": "config/config.go",
    "chars": 1869,
    "preview": "package config\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\n\t\"gopkg.in/yaml.v3\"\n)\n\nvar creators = make(map[string]Creator)\n\n//"
  },
  {
    "path": "config/config_test.go",
    "chars": 1165,
    "preview": "package config\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n)\n\ntype Foo struct {\n\tField1 st"
  },
  {
    "path": "constant/constant.go",
    "chars": 89,
    "preview": "package constant\n\nvar (\n\tVersion = \"Custom Version\"\n\tCommit  = \"Unknown Git Commit ID\"\n)\n"
  },
  {
    "path": "docs/.gitignore",
    "chars": 15,
    "preview": "public/\nthemes/"
  },
  {
    "path": "docs/Makefile",
    "chars": 413,
    "preview": ".PHONY: default clean hugo hugo-build\n\ndefault: hugo\n\nclean:\n\trm -rf public/\n\nhugo-build: clean hugo-themes\n\thugo --enab"
  },
  {
    "path": "docs/archetypes/default.md",
    "chars": 84,
    "preview": "---\ntitle: \"{{ replace .Name \"-\" \" \" | title }}\"\ndate: {{ .Date }}\ndraft: true\n---\n\n"
  },
  {
    "path": "docs/config.toml",
    "chars": 3194,
    "preview": "baseURL = \"https://p4gefau1t.github.io/trojan-go\"\n\nlanguageCode = \"zh-CN\"\ntitle = \"Trojan-Go Docs\"\ntheme = \"hugo-theme-t"
  },
  {
    "path": "docs/content/_index.md",
    "chars": 435,
    "preview": "---\ntitle: \"简介\"\ndraft: false\nweight: 10\n---\n\n# Trojan-Go\n\n这里是Trojan-Go的文档,你可以在左侧的导航栏中找到一些使用技巧,以及完整的配置文件说明。\n\nTrojan-Go是使用"
  },
  {
    "path": "docs/content/advance/_index.md",
    "chars": 103,
    "preview": "---\ntitle: \"高级配置\"\ndraft: false\nweight: 30\n---\n\n这一部分内容将介绍更复杂的Trojan-Go配置方法。对于与Trojan原版不兼容的特性,将在小节中明确标明。\n"
  },
  {
    "path": "docs/content/advance/aead.md",
    "chars": 621,
    "preview": "---\ntitle: \"使用Shadowsocks AEAD进行二次加密\"\ndraft: false\nweight: 8\n---\n\n### 注意,Trojan不支持这个特性\n\nTrojan协议本身无加密,其安全性依赖于下层的TLS。在一般情"
  },
  {
    "path": "docs/content/advance/api.md",
    "chars": 2514,
    "preview": "---\ntitle: \"使用API动态管理用户\"\ndraft: false\nweight: 10\n---\n\n### 注意,Trojan不支持这个特性\n\nTrojan-Go使用gRPC提供了一组API,API支持以下功能:\n\n- 用户信息增删"
  },
  {
    "path": "docs/content/advance/customize-protocol-stack.md",
    "chars": 4805,
    "preview": "---\ntitle: \"自定义协议栈\"\ndraft: false\nweight: 8\n---\n\n### 注意,Trojan不支持这个特性\n\nTrojan-Go允许高级用户自定义协议栈。在自定义模式下,Trojan-Go将放弃对协议栈的控制,"
  },
  {
    "path": "docs/content/advance/forward.md",
    "chars": 1593,
    "preview": "---\ntitle: \"隧道和反向代理\"\ndraft: false\nweight: 5\n---\n\n你可以使用Trojan-Go建立隧道。一个典型的应用是,使用Trojan-Go在本地建立一个无污染的DNS服务器,下面是一个配置的例子\n\n``"
  },
  {
    "path": "docs/content/advance/mux.md",
    "chars": 1069,
    "preview": "---\ntitle: \"启用多路复用提升网络并发性能\"\ndraft: false\nweight: 1\n---\n\n### 注意,Trojan不支持这个特性\n\nTrojan-Go支持使用多路复用提升网络并发性能。\n\nTrojan协议基于TLS。"
  },
  {
    "path": "docs/content/advance/nat.md",
    "chars": 1540,
    "preview": "---\ntitle: \"透明代理\"\ndraft: false\nweight: 11\n---\n\n### 注意,Trojan不完全支持这个特性(UDP)\n\nTrojan-Go支持基于tproxy的透明TCP/UDP代理。\n\n要开启透明代理模式,"
  },
  {
    "path": "docs/content/advance/nginx-relay.md",
    "chars": 3251,
    "preview": "---\ntitle: \"一种基于SNI代理的多路径分流中继方案\"\ndraft: false\nweight: 6\n---\n\n## 前言\n\nTrojan 是一种通过 TLS 封装后进行加密数据传输的工具,利用其 TLS 的特性,我们可以通过 S"
  },
  {
    "path": "docs/content/advance/plugin.md",
    "chars": 1296,
    "preview": "---\ntitle: \"使用Shadowsocks插件/可插拔传输层\"\ndraft: false\nweight: 7\n---\n\n### 注意,Trojan不支持这个特性\n\nTrojan-Go支持可插拔的传输层。原则上,Trojan-Go可以"
  },
  {
    "path": "docs/content/advance/router.md",
    "chars": 2000,
    "preview": "---\ntitle: \"国内直连和广告屏蔽\"\ndraft: false\nweight: 3\n---\n\n### 注意,Trojan不支持这个特性\n\nTrojan-Go内建的路由模块可以帮助你实现国内直连,即客户端对于国内网站不经过代理,直接连"
  },
  {
    "path": "docs/content/advance/websocket.md",
    "chars": 1300,
    "preview": "---\ntitle: \"使用Websocket进行CDN转发和抵抗中间人攻击\"\ndraft: false\nweight: 2\n---\n\n### 注意,Trojan不支持这个特性\n\nTrojan-Go支持使用TLS+Websocket承载Tr"
  },
  {
    "path": "docs/content/basic/_index.md",
    "chars": 83,
    "preview": "---\ntitle: \"基本配置\"\ndraft: false\nweight: 20\n---\n\n这一部分内容将介绍如何配置基本的Trojan-Go代理服务器和客户端。\n"
  },
  {
    "path": "docs/content/basic/config.md",
    "chars": 2894,
    "preview": "---\ntitle: \"正确配置Trojan-Go\"\ndraft: false\nweight: 22\n---\n\n下面将介绍如何正确配置Trojan-Go以完全隐藏你的代理节点特征。\n\n在开始之前,你需要\n\n- 一个服务器,且未被GFW封锁\n"
  },
  {
    "path": "docs/content/basic/full-config.md",
    "chars": 9902,
    "preview": "---\ntitle: \"完整的配置文件\"\ndraft: false\nweight: 30\n---\n\n下面是一个完整的配置文件,其中的必填选项有\n\n- ```run_type```\n\n- ```local_addr```\n\n- ```loca"
  },
  {
    "path": "docs/content/basic/trojan.md",
    "chars": 1397,
    "preview": "---\ntitle: \"Trojan基本原理\"\ndraft: false\nweight: 21\n---\n\n这个页面将会简单讲述Trojan协议的基本工作原理。如果你对于GFW和Trojan的工作方式不感兴趣,可以跳过这一小节。但为了更好地保"
  },
  {
    "path": "docs/content/developer/_index.md",
    "chars": 84,
    "preview": "---\ntitle: \"实现细节和开发指南\"\ndraft: false\nweight: 40\n---\n\n这一部分介绍Trojan-Go底层实现的细节,主要面向开发者。\n"
  },
  {
    "path": "docs/content/developer/api.md",
    "chars": 565,
    "preview": "---\ntitle: \"API开发\"\ndraft: false\nweight: 100\n---\n\nTrojan-Go基于gRPC实现了API,使用protobuf交换数据。客户端可获取流量和速度信息;服务端可获取各用户流量,速度,在线情况,"
  },
  {
    "path": "docs/content/developer/build.md",
    "chars": 826,
    "preview": "---\ntitle: \"编译和自定义Trojan-Go\"\ndraft: false\nweight: 10\n---\n\n编译需要Go版本号高于1.14.x,请在编译前确认你的编译器版本。推荐使用snap安装和更新go。\n\n编译方式非常简单,可以"
  },
  {
    "path": "docs/content/developer/mux.md",
    "chars": 484,
    "preview": "---\ntitle: \"多路复用\"\ndraft: false\nweight: 30\n---\n\nTrojan-Go使用[smux](https://github.com/xtaci/smux)实现多路复用。同时实现了simplesocks协议"
  },
  {
    "path": "docs/content/developer/overview.md",
    "chars": 2429,
    "preview": "---\ntitle: \"基本介绍\"\ndraft: false\nweight: 1\n---\n\nTrojan-Go的核心部分有\n\n- tunnel 各个协议具体实现\n\n- proxy 代理核心\n\n- config 配置注册和解析模块\n\n- re"
  },
  {
    "path": "docs/content/developer/plugin.md",
    "chars": 1233,
    "preview": "---\ntitle: \"可插拔传输层插件开发\"\ndraft: false\nweight: 150\n---\n\nTrojan-Go鼓励开发传输层插件,以丰富协议类型,增加与GFW对抗的战略纵深。\n\n传输层插件的作用,是替代tansport隧道的"
  },
  {
    "path": "docs/content/developer/simplesocks.md",
    "chars": 500,
    "preview": "---\ntitle: \"SimpleSocks协议\"\ndraft: false\nweight: 50\n---\n\nSimpleSocks协议是无鉴权机制的简单代理协议,本质上是去除了sha224的Trojan协议。使用该协议的目的是减少多路复"
  },
  {
    "path": "docs/content/developer/trojan.md",
    "chars": 251,
    "preview": "---\ntitle: \"Trojan协议\"\ndraft: false\nweight: 20\n---\n\nTrojan-Go遵循原始的trojan协议,具体格式可以参考[Trojan文档](https://trojan-gfw.github.i"
  },
  {
    "path": "docs/content/developer/url.md",
    "chars": 2886,
    "preview": "---\ntitle: \"URL方案(草案)\"\ndraft: false\nweight: 200\n---\n\n## Changelog\n\n- encryption 格式修改为 ss;method:password\n\n## 概述\n\n感谢 @Duc"
  },
  {
    "path": "docs/content/developer/websocket.md",
    "chars": 573,
    "preview": "---\ntitle: \"Websocket\"\ndraft: false\nweight: 40\n---\n\n由于使用CDN中转时,HTTPS对CDN透明,CDN可以审查Websocket传输内容。而Trojan协议本身是明文传输,因此为保证安全"
  },
  {
    "path": "easy/easy.go",
    "chars": 4504,
    "preview": "package easy\n\nimport (\n\t\"encoding/json\"\n\t\"flag\"\n\t\"net\"\n\t\"strconv\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com"
  },
  {
    "path": "example/client.json",
    "chars": 754,
    "preview": "{\n    \"run_type\": \"client\",\n    \"local_addr\": \"127.0.0.1\",\n    \"local_port\": 1080,\n    \"remote_addr\": \"your_server\",\n   "
  },
  {
    "path": "example/client.yaml",
    "chars": 476,
    "preview": "run-type: client\nlocal-addr: 127.0.0.1\nlocal-port: 1080\nremote-addr: your_server\nremote-port: 443\npassword:\n    - your_p"
  },
  {
    "path": "example/server.json",
    "chars": 517,
    "preview": "{\n    \"run_type\": \"server\",\n    \"local_addr\": \"0.0.0.0\",\n    \"local_port\": 443,\n    \"remote_addr\": \"127.0.0.1\",\n    \"rem"
  },
  {
    "path": "example/server.yaml",
    "chars": 334,
    "preview": "run-type: server\nlocal-addr: 0.0.0.0\nlocal-port: 443\nremote-addr: 127.0.0.1\nremote-port: 80\npassword:\n  - your_password\n"
  },
  {
    "path": "example/trojan-go.service",
    "chars": 490,
    "preview": "[Unit]\nDescription=Trojan-Go - An unidentifiable mechanism that helps you bypass GFW\nDocumentation=https://p4gefau1t.git"
  },
  {
    "path": "example/trojan-go@.service",
    "chars": 486,
    "preview": "[Unit]\nDescription=Trojan-Go - An unidentifiable mechanism that helps you bypass GFW\nDocumentation=https://p4gefau1t.git"
  },
  {
    "path": "go.mod",
    "chars": 1744,
    "preview": "module github.com/p4gefau1t/trojan-go\n\ngo 1.17\n\nrequire (\n\tgithub.com/go-sql-driver/mysql v1.6.0\n\tgithub.com/refraction-"
  },
  {
    "path": "go.sum",
    "chars": 20008,
    "preview": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1"
  },
  {
    "path": "log/golog/buffer/buffer.go",
    "chars": 856,
    "preview": "// Buffer-like byte slice\n// Copyright (c) 2017 Fadhli Dzil Ikram\n\npackage buffer\n\n// Buffer type wrap up byte slice bui"
  },
  {
    "path": "log/golog/buffer/buffer_test.go",
    "chars": 1640,
    "preview": "// Buffer-like byte slice\n// Copyright (c) 2017 Fadhli Dzil Ikram\n//\n// Test file for buffer\n\npackage buffer\n\nimport (\n\t"
  },
  {
    "path": "log/golog/colorful/colorful.go",
    "chars": 2537,
    "preview": "// The color engine for the go-log library\n// Copyright (c) 2017 Fadhli Dzil Ikram\n\npackage colorful\n\nimport (\n\t\"runtime"
  },
  {
    "path": "log/golog/colorful/colorful_test.go",
    "chars": 2957,
    "preview": "// The color engine for the go-log library\n// Copyright (c) 2017 Fadhli Dzil Ikram\n//\n// Test file\n\npackage colorful\n\nim"
  },
  {
    "path": "log/golog/golog.go",
    "chars": 8688,
    "preview": "// The colorful and simple logging library\n// Copyright (c) 2017 Fadhli Dzil Ikram\n\npackage golog\n\nimport (\n\t\"fmt\"\n\t\"io\""
  },
  {
    "path": "log/log.go",
    "chars": 2607,
    "preview": "package log\n\nimport (\n\t\"io\"\n\t\"os\"\n)\n\n// LogLevel how much log to dump\n// 0: ALL; 1: INFO; 2: WARN; 3: ERROR; 4: FATAL; 5"
  },
  {
    "path": "log/simplelog/simplelog.go",
    "chars": 1833,
    "preview": "package simplelog\n\nimport (\n\t\"io\"\n\tgolog \"log\"\n\t\"os\"\n\n\t\"github.com/p4gefau1t/trojan-go/log\"\n)\n\nfunc init() {\n\tlog.Regist"
  },
  {
    "path": "main.go",
    "chars": 342,
    "preview": "package main\n\nimport (\n\t\"flag\"\n\n\t_ \"github.com/p4gefau1t/trojan-go/component\"\n\t\"github.com/p4gefau1t/trojan-go/log\"\n\t\"gi"
  },
  {
    "path": "option/option.go",
    "chars": 572,
    "preview": "package option\n\nimport \"github.com/p4gefau1t/trojan-go/common\"\n\ntype Handler interface {\n\tName() string\n\tHandle() error\n"
  },
  {
    "path": "proxy/client/client.go",
    "chars": 2263,
    "preview": "package client\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/config\"\n\t\"github.com/p4gefau1t/trojan-go/proxy\"\n\t\""
  },
  {
    "path": "proxy/client/config.go",
    "chars": 955,
    "preview": "package client\n\nimport \"github.com/p4gefau1t/trojan-go/config\"\n\ntype MuxConfig struct {\n\tEnabled bool `json:\"enabled\" ya"
  },
  {
    "path": "proxy/config.go",
    "chars": 357,
    "preview": "package proxy\n\nimport \"github.com/p4gefau1t/trojan-go/config\"\n\ntype Config struct {\n\tRunType  string `json:\"run_type\" ya"
  },
  {
    "path": "proxy/custom/config.go",
    "chars": 620,
    "preview": "package custom\n\nimport \"github.com/p4gefau1t/trojan-go/config\"\n\nconst Name = \"CUSTOM\"\n\ntype NodeConfig struct {\n\tProtoco"
  },
  {
    "path": "proxy/custom/custom.go",
    "chars": 3398,
    "preview": "package custom\n\nimport (\n\t\"context\"\n\t\"strings\"\n\n\t\"gopkg.in/yaml.v3\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.c"
  },
  {
    "path": "proxy/forward/forward.go",
    "chars": 1068,
    "preview": "package forward\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/config\"\n\t\"github.com/p4gefau1t/trojan-go/proxy\"\n\t"
  },
  {
    "path": "proxy/nat/nat.go",
    "chars": 1216,
    "preview": "//go:build linux\n// +build linux\n\npackage nat\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.co"
  },
  {
    "path": "proxy/nat/nat_stub.go",
    "chars": 12,
    "preview": "package nat\n"
  },
  {
    "path": "proxy/option.go",
    "chars": 3223,
    "preview": "package proxy\n\nimport (\n\t\"bufio\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"github.com/p4gefau1t/trojan-"
  },
  {
    "path": "proxy/proxy.go",
    "chars": 4534,
    "preview": "package proxy\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"math/rand\"\n\t\"net\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n"
  },
  {
    "path": "proxy/server/config.go",
    "chars": 221,
    "preview": "package server\n\nimport (\n\t\"github.com/p4gefau1t/trojan-go/config\"\n\t\"github.com/p4gefau1t/trojan-go/proxy/client\"\n)\n\nfunc"
  },
  {
    "path": "proxy/server/server.go",
    "chars": 2121,
    "preview": "package server\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/config\"\n\t\"github.com/p4gefau1t/trojan-go/proxy\"\n\t\""
  },
  {
    "path": "proxy/stack.go",
    "chars": 2105,
    "preview": "package proxy\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/log\"\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\nty"
  },
  {
    "path": "redirector/redirector.go",
    "chars": 2301,
    "preview": "package redirector\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\t\"reflect\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/p"
  },
  {
    "path": "redirector/redirector_test.go",
    "chars": 1409,
    "preview": "package redirector\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/p4gefau1t/"
  },
  {
    "path": "statistic/memory/config.go",
    "chars": 243,
    "preview": "package memory\n\nimport (\n\t\"github.com/p4gefau1t/trojan-go/config\"\n)\n\ntype Config struct {\n\tPasswords []string `json:\"pas"
  },
  {
    "path": "statistic/memory/memory.go",
    "chars": 5029,
    "preview": "package memory\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"golang.org/x/time/rate\"\n\n\t\"github.com/p4gefau1t/tr"
  },
  {
    "path": "statistic/memory/memory_test.go",
    "chars": 2934,
    "preview": "package memory\n\nimport (\n\t\"context\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t"
  },
  {
    "path": "statistic/mysql/config.go",
    "chars": 723,
    "preview": "package mysql\n\nimport (\n\t\"github.com/p4gefau1t/trojan-go/config\"\n)\n\ntype MySQLConfig struct {\n\tEnabled    bool   `json:\""
  },
  {
    "path": "statistic/mysql/mysql.go",
    "chars": 2961,
    "preview": "package mysql\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t// MySQL Driver\n\t_ \"github.com/go-sql-dri"
  },
  {
    "path": "statistic/statistics.go",
    "chars": 1671,
    "preview": "package statistic\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/p"
  },
  {
    "path": "test/scenario/custom_test.go",
    "chars": 5132,
    "preview": "package scenario\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t_ \"github.com/p4gefau1t/trojan-g"
  },
  {
    "path": "test/scenario/proxy_test.go",
    "chars": 13225,
    "preview": "package scenario\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t_ \"net/http/pprof\"\n\t\"os\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\tnet"
  },
  {
    "path": "test/util/target.go",
    "chars": 3282,
    "preview": "package util\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"net/http\"\n\t\"sync\"\n\t\"time\"\n\n\t\"golang.org/x/net/w"
  },
  {
    "path": "test/util/util.go",
    "chars": 2161,
    "preview": "package util\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"net\"\n\t\"sync\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n)\n\n// Che"
  },
  {
    "path": "tunnel/adapter/config.go",
    "chars": 299,
    "preview": "package adapter\n\nimport \"github.com/p4gefau1t/trojan-go/config\"\n\ntype Config struct {\n\tLocalHost string `json:\"local_add"
  },
  {
    "path": "tunnel/adapter/server.go",
    "chars": 3127,
    "preview": "package adapter\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"sync\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/p4gefau1t/tro"
  },
  {
    "path": "tunnel/adapter/tunnel.go",
    "chars": 484,
    "preview": "package adapter\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\nconst Name = \"ADAPTER\"\n\ntype Tunnel st"
  },
  {
    "path": "tunnel/dokodemo/config.go",
    "chars": 499,
    "preview": "package dokodemo\n\nimport \"github.com/p4gefau1t/trojan-go/config\"\n\ntype Config struct {\n\tLocalHost  string `json:\"local_a"
  },
  {
    "path": "tunnel/dokodemo/conn.go",
    "chars": 1527,
    "preview": "package dokodemo\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/p4gefau1t/trojan-go/"
  },
  {
    "path": "tunnel/dokodemo/dokodemo_test.go",
    "chars": 2036,
    "preview": "package dokodemo\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"githu"
  },
  {
    "path": "tunnel/dokodemo/server.go",
    "chars": 3652,
    "preview": "package dokodemo\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/p4ge"
  },
  {
    "path": "tunnel/dokodemo/tunnel.go",
    "chars": 564,
    "preview": "package dokodemo\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/p4gefau1t/trojan-go/tunnel\""
  },
  {
    "path": "tunnel/freedom/client.go",
    "chars": 3547,
    "preview": "package freedom\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"github.com/txthinking/socks5\"\n\t\"golang.org/x/net/proxy\"\n\n\t\"github.com/p4g"
  },
  {
    "path": "tunnel/freedom/config.go",
    "chars": 1048,
    "preview": "package freedom\n\nimport \"github.com/p4gefau1t/trojan-go/config\"\n\ntype Config struct {\n\tLocalHost    string             `"
  },
  {
    "path": "tunnel/freedom/conn.go",
    "chars": 2426,
    "preview": "package freedom\n\nimport (\n\t\"bytes\"\n\t\"net\"\n\n\t\"github.com/txthinking/socks5\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"g"
  },
  {
    "path": "tunnel/freedom/freedom_test.go",
    "chars": 2450,
    "preview": "package freedom\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/txthinking/socks5\"\n\n\t\"github.com/p"
  },
  {
    "path": "tunnel/freedom/tunnel.go",
    "chars": 478,
    "preview": "package freedom\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\nconst Name = \"FREEDOM\"\n\ntype Tunnel st"
  },
  {
    "path": "tunnel/http/http_test.go",
    "chars": 1955,
    "preview": "package http\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/p4gef"
  },
  {
    "path": "tunnel/http/server.go",
    "chars": 4743,
    "preview": "package http\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/p4gefau"
  },
  {
    "path": "tunnel/http/tunnel.go",
    "chars": 478,
    "preview": "package http\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\nconst Name = \"HTTP\"\n\ntype Tunnel struct{}"
  },
  {
    "path": "tunnel/metadata.go",
    "chars": 4663,
    "preview": "package tunnel\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"strconv\"\n\n\t\"github.com/p4gefau1t/trojan-go/co"
  },
  {
    "path": "tunnel/mux/client.go",
    "chars": 4672,
    "preview": "package mux\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/xtaci/smux\"\n\n\t\"github.com/p4gefau1t/t"
  },
  {
    "path": "tunnel/mux/config.go",
    "chars": 508,
    "preview": "package mux\n\nimport \"github.com/p4gefau1t/trojan-go/config\"\n\ntype MuxConfig struct {\n\tEnabled     bool `json:\"enabled\" y"
  },
  {
    "path": "tunnel/mux/conn.go",
    "chars": 1894,
    "preview": "package mux\n\nimport (\n\t\"io\"\n\t\"math/rand\"\n\n\t\"github.com/p4gefau1t/trojan-go/log\"\n\t\"github.com/p4gefau1t/trojan-go/tunnel\""
  },
  {
    "path": "tunnel/mux/mux_test.go",
    "chars": 1416,
    "preview": "package mux\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/p4gefau1t/trojan-go/c"
  },
  {
    "path": "tunnel/mux/server.go",
    "chars": 1898,
    "preview": "package mux\n\nimport (\n\t\"context\"\n\n\t\"github.com/xtaci/smux\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/p4gefa"
  },
  {
    "path": "tunnel/mux/tunnel.go",
    "chars": 477,
    "preview": "package mux\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\nconst Name = \"MUX\"\n\ntype Tunnel struct{}\n\n"
  },
  {
    "path": "tunnel/router/client.go",
    "chars": 11868,
    "preview": "package router\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\n\tv2router \"github.com/v2fly/v2ray"
  },
  {
    "path": "tunnel/router/config.go",
    "chars": 1018,
    "preview": "package router\n\nimport (\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/p4gefau1t/trojan-go/config\"\n)\n\ntype Confi"
  },
  {
    "path": "tunnel/router/conn.go",
    "chars": 2291,
    "preview": "package router\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/p4gefau1t/trojan"
  },
  {
    "path": "tunnel/router/data.go",
    "chars": 15,
    "preview": "package router\n"
  },
  {
    "path": "tunnel/router/router_test.go",
    "chars": 3556,
    "preview": "package router\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/p4gefau1t/trojan-go/co"
  },
  {
    "path": "tunnel/router/tunnel.go",
    "chars": 482,
    "preview": "package router\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\nconst Name = \"ROUTER\"\n\ntype Tunnel stru"
  },
  {
    "path": "tunnel/shadowsocks/client.go",
    "chars": 1169,
    "preview": "package shadowsocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/shadowsocks/go-shadowsocks2/core\"\n\n\t\"github.com/p4gefau1t/trojan-"
  },
  {
    "path": "tunnel/shadowsocks/config.go",
    "chars": 655,
    "preview": "package shadowsocks\n\nimport \"github.com/p4gefau1t/trojan-go/config\"\n\ntype ShadowsocksConfig struct {\n\tEnabled  bool   `j"
  },
  {
    "path": "tunnel/shadowsocks/conn.go",
    "chars": 450,
    "preview": "package shadowsocks\n\nimport (\n\t\"net\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\ntype Conn struct {\n\taeadConn net.Conn\n"
  },
  {
    "path": "tunnel/shadowsocks/server.go",
    "chars": 2290,
    "preview": "package shadowsocks\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"github.com/shadowsocks/go-shadowsocks2/core\"\n\n\t\"github.com/p4gefau1t/"
  },
  {
    "path": "tunnel/shadowsocks/shadowsocks_test.go",
    "chars": 2131,
    "preview": "package shadowsocks\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/p4gefau1t/"
  },
  {
    "path": "tunnel/shadowsocks/tunnel.go",
    "chars": 499,
    "preview": "package shadowsocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\nconst Name = \"SHADOWSOCKS\"\n\ntype T"
  },
  {
    "path": "tunnel/simplesocks/client.go",
    "chars": 1567,
    "preview": "package simplesocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/p4gefau1t/trojan-go/log\""
  },
  {
    "path": "tunnel/simplesocks/conn.go",
    "chars": 934,
    "preview": "package simplesocks\n\nimport (\n\t\"bytes\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/p4gefau1t/trojan-go/tunnel"
  },
  {
    "path": "tunnel/simplesocks/server.go",
    "chars": 2183,
    "preview": "package simplesocks\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/p4gefau1t/trojan-"
  },
  {
    "path": "tunnel/simplesocks/simplesocks_test.go",
    "chars": 1844,
    "preview": "package simplesocks\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/p4gefa"
  },
  {
    "path": "tunnel/simplesocks/tunnel.go",
    "chars": 501,
    "preview": "package simplesocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\nconst Name = \"SIMPLESOCKS\"\n\ntype T"
  },
  {
    "path": "tunnel/socks/config.go",
    "chars": 378,
    "preview": "package socks\n\nimport \"github.com/p4gefau1t/trojan-go/config\"\n\ntype Config struct {\n\tLocalHost  string `json:\"local_addr"
  },
  {
    "path": "tunnel/socks/conn.go",
    "chars": 1283,
    "preview": "package socks\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/p4gefau1t/trojan-go/tun"
  },
  {
    "path": "tunnel/socks/server.go",
    "chars": 7020,
    "preview": "package socks\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/p4gefau1t/tro"
  },
  {
    "path": "tunnel/socks/socks_test.go",
    "chars": 3279,
    "preview": "package socks_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/txt"
  },
  {
    "path": "tunnel/socks/tunnel.go",
    "chars": 463,
    "preview": "package socks\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\nconst Name = \"SOCKS\"\n\ntype Tunnel struct"
  },
  {
    "path": "tunnel/tls/client.go",
    "chars": 3942,
    "preview": "package tls\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"strings\"\n\n\tutls \"gith"
  },
  {
    "path": "tunnel/tls/config.go",
    "chars": 1863,
    "preview": "package tls\n\nimport (\n\t\"github.com/p4gefau1t/trojan-go/config\"\n)\n\ntype Config struct {\n\tRemoteHost string          `json"
  },
  {
    "path": "tunnel/tls/fingerprint/tls.go",
    "chars": 403,
    "preview": "package fingerprint\n\nimport (\n\t\"crypto/tls\"\n\n\t\"github.com/p4gefau1t/trojan-go/log\"\n)\n\nfunc ParseCipher(s []string) []uin"
  },
  {
    "path": "tunnel/tls/server.go",
    "chars": 10962,
    "preview": "package tls\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net"
  },
  {
    "path": "tunnel/tls/tls_test.go",
    "chars": 10970,
    "preview": "package tls\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"os\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/"
  },
  {
    "path": "tunnel/tls/tunnel.go",
    "chars": 483,
    "preview": "package tls\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\nconst Name = \"TLS\"\n\ntype Tunnel struct{}\n\n"
  },
  {
    "path": "tunnel/tproxy/config.go",
    "chars": 413,
    "preview": "//go:build linux\n// +build linux\n\npackage tproxy\n\nimport \"github.com/p4gefau1t/trojan-go/config\"\n\ntype Config struct {\n\t"
  },
  {
    "path": "tunnel/tproxy/conn.go",
    "chars": 1318,
    "preview": "//go:build linux\n// +build linux\n\npackage tproxy\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t"
  },
  {
    "path": "tunnel/tproxy/getsockopt.go",
    "chars": 392,
    "preview": "//go:build linux && !386\n// +build linux,!386\n\npackage tproxy\n\nimport (\n\t\"syscall\"\n\t\"unsafe\"\n)\n\nfunc getsockopt(fd int, "
  },
  {
    "path": "tunnel/tproxy/getsockopt_i386.go",
    "chars": 401,
    "preview": "//go:build linux && 386\n// +build linux,386\n\npackage tproxy\n\nimport (\n\t\"syscall\"\n\t\"unsafe\"\n)\n\nconst GETSOCKOPT = 15\n\nfun"
  },
  {
    "path": "tunnel/tproxy/server.go",
    "chars": 5937,
    "preview": "//go:build linux\n// +build linux\n\npackage tproxy\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/p4gefau"
  },
  {
    "path": "tunnel/tproxy/tcp.go",
    "chars": 3531,
    "preview": "//go:build linux\n// +build linux\n\npackage tproxy\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"syscall\"\n\t\"unsafe\"\n)\n\n// Listener descr"
  },
  {
    "path": "tunnel/tproxy/tproxy_stub.go",
    "chars": 15,
    "preview": "package tproxy\n"
  },
  {
    "path": "tunnel/tproxy/tunnel.go",
    "chars": 516,
    "preview": "//go:build linux\n// +build linux\n\npackage tproxy\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\nconst"
  },
  {
    "path": "tunnel/tproxy/udp.go",
    "chars": 6287,
    "preview": "//go:build linux\n// +build linux\n\npackage tproxy\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"strconv\"\n\t\""
  },
  {
    "path": "tunnel/transport/client.go",
    "chars": 3049,
    "preview": "package transport\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strconv\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.co"
  },
  {
    "path": "tunnel/transport/config.go",
    "chars": 910,
    "preview": "package transport\n\nimport (\n\t\"github.com/p4gefau1t/trojan-go/config\"\n)\n\ntype Config struct {\n\tLocalHost       string    "
  },
  {
    "path": "tunnel/transport/conn.go",
    "chars": 171,
    "preview": "package transport\n\nimport (\n\t\"net\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\ntype Conn struct {\n\tnet.Conn\n}\n\nfunc (c "
  },
  {
    "path": "tunnel/transport/server.go",
    "chars": 4622,
    "preview": "package transport\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"net\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github"
  },
  {
    "path": "tunnel/transport/transport_test.go",
    "chars": 2483,
    "preview": "package transport\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\"github.com/"
  },
  {
    "path": "tunnel/transport/tunnel.go",
    "chars": 489,
    "preview": "package transport\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\nconst Name = \"TRANSPORT\"\n\ntype Tunne"
  },
  {
    "path": "tunnel/trojan/client.go",
    "chars": 4288,
    "preview": "package trojan\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"net\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/p4gefau1t/trojan-go/api"
  },
  {
    "path": "tunnel/trojan/config.go",
    "chars": 804,
    "preview": "package trojan\n\nimport \"github.com/p4gefau1t/trojan-go/config\"\n\ntype Config struct {\n\tLocalHost        string      `json"
  },
  {
    "path": "tunnel/trojan/packet.go",
    "chars": 2259,
    "preview": "package trojan\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\""
  },
  {
    "path": "tunnel/trojan/server.go",
    "chars": 6535,
    "preview": "package trojan\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"sync/atomic\"\n\n\t\"github.com/p4gefau1t/trojan-go/api\"\n\t\"github.c"
  },
  {
    "path": "tunnel/trojan/trojan_test.go",
    "chars": 2779,
    "preview": "package trojan\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"testing\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n\t\""
  },
  {
    "path": "tunnel/trojan/tunnel.go",
    "chars": 489,
    "preview": "package trojan\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\nconst Name = \"TROJAN\"\n\ntype Tunnel stru"
  },
  {
    "path": "tunnel/tunnel.go",
    "chars": 2097,
    "preview": "package tunnel\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\n\t\"github.com/p4gefau1t/trojan-go/common\"\n)\n\n// Conn is the TCP connect"
  },
  {
    "path": "tunnel/websocket/client.go",
    "chars": 1717,
    "preview": "package websocket\n\nimport (\n\t\"context\"\n\t\"strings\"\n\n\t\"golang.org/x/net/websocket\"\n\n\t\"github.com/p4gefau1t/trojan-go/commo"
  },
  {
    "path": "tunnel/websocket/config.go",
    "chars": 554,
    "preview": "package websocket\n\nimport \"github.com/p4gefau1t/trojan-go/config\"\n\ntype WebsocketConfig struct {\n\tEnabled bool   `json:\""
  },
  {
    "path": "tunnel/websocket/conn.go",
    "chars": 589,
    "preview": "package websocket\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"golang.org/x/net/websocket\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)"
  },
  {
    "path": "tunnel/websocket/server.go",
    "chars": 4951,
    "preview": "package websocket\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"math/rand\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"golang.org/x/net/w"
  },
  {
    "path": "tunnel/websocket/tunnel.go",
    "chars": 497,
    "preview": "package websocket\n\nimport (\n\t\"context\"\n\n\t\"github.com/p4gefau1t/trojan-go/tunnel\"\n)\n\nconst Name = \"WEBSOCKET\"\n\ntype Tunne"
  },
  {
    "path": "tunnel/websocket/websocket_test.go",
    "chars": 2688,
    "preview": "package websocket\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"golang.org/x/net/websocket"
  }
]

// ... and 5 more files (download for full content)

About this extraction

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

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

Copied to clipboard!